├── .Procfile ├── .gitignore ├── README.md ├── lcc-web ├── pom.xml └── src │ ├── lccf.sql │ ├── main │ ├── java │ │ └── com │ │ │ └── lccf │ │ │ ├── Application.java │ │ │ ├── aop │ │ │ └── BindingResultAop.java │ │ │ ├── config │ │ │ ├── LocaleConfiguration.java │ │ │ ├── MappingConfiguration.java │ │ │ ├── ThymeleafConfiguration.java │ │ │ ├── apidoc │ │ │ │ └── SwaggerConfiguration.java │ │ │ ├── redis │ │ │ │ └── CacheConfig.java │ │ │ └── security │ │ │ │ └── WebSecurityConfig.java │ │ │ ├── controller │ │ │ ├── EmailController.java │ │ │ ├── GraphqlController.java │ │ │ ├── MenuController.java │ │ │ └── UserController.java │ │ │ ├── domain │ │ │ ├── JltEmailLog.java │ │ │ ├── JltEmailTpl.java │ │ │ ├── Menu.java │ │ │ └── User.java │ │ │ ├── exception │ │ │ ├── BusinessException.java │ │ │ ├── ExceptionConst.java │ │ │ └── ExceptionTranslator.java │ │ │ ├── filter │ │ │ ├── JWTAuthenticationFilter.java │ │ │ ├── JWTLoginFilter.java │ │ │ └── UserParam.java │ │ │ ├── param │ │ │ └── EmailReq.java │ │ │ ├── repository │ │ │ ├── JltEmialLogRepository.java │ │ │ ├── JltEmialTplRepository.java │ │ │ ├── MenuRepostiory.java │ │ │ └── UserRepository.java │ │ │ └── service │ │ │ ├── mail │ │ │ ├── JltEmailService.java │ │ │ ├── MailService.java │ │ │ └── impl │ │ │ │ └── JltEmailServiceImpl.java │ │ │ ├── menu │ │ │ ├── IMenuService.java │ │ │ ├── MenuParam.java │ │ │ ├── MenuVo.java │ │ │ └── impl │ │ │ │ ├── MenuServiceImpl.java │ │ │ │ └── MenuUtil.java │ │ │ ├── security │ │ │ ├── CustomAccessDeniedHandler.java │ │ │ ├── TokenAuthenticationService.java │ │ │ ├── UserDetailsService.java │ │ │ └── UserNotActivatedException.java │ │ │ └── user │ │ │ ├── IUserService.java │ │ │ ├── UserParam.java │ │ │ ├── UserVo.java │ │ │ └── impl │ │ │ └── UserServiceImpl.java │ └── resources │ │ ├── application-dev.yml │ │ ├── application-prod.yml │ │ ├── application-test.yml │ │ ├── application.properties │ │ ├── i18n │ │ ├── messages_en.properties │ │ └── messages_zh_CN.properties │ │ ├── log4j2.yaml │ │ └── mails │ │ ├── activationEmail.html │ │ ├── creationEmail.html │ │ ├── passwordResetEmail.html │ │ └── tradeEmail.html │ └── test │ └── java │ └── com │ └── lccf │ ├── Documentation.java │ └── Swagger2MarkupResultHandlerUtf8.java ├── lccf-base ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── lccf │ └── base │ ├── controller │ ├── BaseController.java │ └── DownloadController.java │ ├── param │ ├── BaseParam.java │ └── PageParam.java │ └── service │ ├── IBaseService.java │ └── impl │ └── BaseServiceImpl.java ├── lccf-util ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── lccf │ ├── constants │ ├── Constants.java │ └── LccfProperties.java │ ├── enums │ ├── ECustomUUID.java │ ├── EResultCode.java │ ├── ETrade.java │ ├── EUserStatus.java │ └── StatusEmun.java │ └── util │ ├── BeanMapper.java │ ├── Collections3.java │ ├── CookieUtil.java │ ├── CustomUUID.java │ ├── GenericsUtils.java │ ├── Reflect.java │ ├── ReflectException.java │ ├── Reflections.java │ ├── ResponseVo.java │ ├── ResponseVoUtil.java │ ├── SpringUtil.java │ ├── StringUtil.java │ ├── TimeUtil.java │ ├── TranslatorHelper.java │ ├── WebUtils.java │ └── excel │ ├── CsvUtil.java │ ├── ExcelUtil.java │ ├── ExcelVO.java │ ├── ExcelVOAttribute.java │ └── ExcelValidAttribute.java └── pom.xml /.Procfile: -------------------------------------------------------------------------------- 1 | web:java -jar lcc-web/target/lccf-web-0.0.1-SNAPSHOT.jar --server.port=8070 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 2 | 最近在看spring boot 觉得还是蛮强大的,所以一套基于spring boot的框架,把一些知识点都整合进去 3 | > ## 框架说明 4 | > - **权限:** ```JWT+SpringSecurity+cookie``` 5 | > - **统一异常:** [ExceptionTranslator](lcc-web/src/main/java/com/lccf/exception/ExceptionTranslator.java) 6 | > - **缓存:** ```内存缓存采用guava nosql采用redis 详见CacheConfig``` 7 | > - **发送邮件** 8 | > - **公用service封装:**[BaseServiceImpl](lccf-service/src/main/java/com/lccf/service/base/impl/BaseServiceImpl.java) 9 | > - **swagger2 接口文档导出** 10 | > - **统一参数校验:** [BindingResultAop](lcc-web/src/main/java/com/lccf/aop/BindingResultAop.java) 11 | > - **excel工具类:** 12 | 13 | -------------------------------------------------------------------------------- /lcc-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | lccf 7 | com.lccf 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.lccf.web 13 | lccf-web 14 | war 15 | 16 | 17 | 18 | com.lccf.base 19 | base 20 | ${project.version} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-maven-plugin 84 | 85 | 86 | org.springframework 87 | springloaded 88 | 1.2.6.RELEASE 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /lcc-web/src/lccf.sql: -------------------------------------------------------------------------------- 1 | -- 菜单sql 2 | INSERT INTO `menu` VALUES ('1', '\0', '\0', 'ion-android-home', null, 'dashboard', '\0', '首页', '0'); 3 | INSERT INTO `menu` VALUES ('4', '\0', '\0', 'ion-person-stalker', null, 'user', '\0', '用户管理', '100'); 4 | INSERT INTO `menu` VALUES ('5', '\0', '\0', null, '4', 'userList', '\0', '用户列表', '200'); 5 | INSERT INTO `menu` VALUES ('6', '\0', '\0', 'ion-social-windows', null, 'sys', '\0', '系统管理', '50'); 6 | INSERT INTO `menu` VALUES ('7', '\0', '\0', null, '6', 'menu', '\0', '菜单管理', '60'); -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/Application.java: -------------------------------------------------------------------------------- 1 | 2 | package com.lccf; 3 | 4 | import com.lccf.config.apidoc.SwaggerConfiguration; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.boot.builder.SpringApplicationBuilder; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.boot.web.support.SpringBootServletInitializer; 10 | import org.springframework.context.annotation.ComponentScan; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.context.annotation.Import; 13 | 14 | import com.lccf.config.LocaleConfiguration; 15 | import com.lccf.config.MappingConfiguration; 16 | import com.lccf.config.security.WebSecurityConfig; 17 | import com.lccf.constants.LccfProperties; 18 | import com.lccf.util.SpringUtil; 19 | 20 | @Configuration 21 | @EnableAutoConfiguration 22 | @EnableConfigurationProperties({ LccfProperties.class }) 23 | @Import({ 24 | SpringUtil.class, 25 | WebSecurityConfig.class, 26 | LocaleConfiguration.class, 27 | SwaggerConfiguration.class, 28 | MappingConfiguration.class 29 | 30 | }) 31 | @ComponentScan("com.lccf") 32 | public class Application extends SpringBootServletInitializer { 33 | 34 | @Override 35 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 36 | return application.sources(Application.class); 37 | } 38 | 39 | public static void main(String[] args) throws Exception { 40 | SpringApplication.run(Application.class, args); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/aop/BindingResultAop.java: -------------------------------------------------------------------------------- 1 | package com.lccf.aop; 2 | 3 | import com.lccf.util.ResponseVoUtil; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.aspectj.lang.annotation.Around; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.aspectj.lang.annotation.Pointcut; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.validation.BindingResult; 14 | import org.springframework.web.bind.MethodArgumentNotValidException; 15 | 16 | 17 | /** 18 | * 参数验证处理类 19 | * @author lichangchao 20 | */ 21 | @Aspect 22 | @Component 23 | public class BindingResultAop { 24 | 25 | 26 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 27 | 28 | @Pointcut("execution(* com.lccf..*.*(..))") 29 | public void aopMethod() { 30 | } 31 | 32 | 33 | @Around("aopMethod()") 34 | public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 35 | LOG.info("before method invoking!"); 36 | BindingResult bindingResult = null; 37 | for (Object arg : joinPoint.getArgs()) { 38 | if (arg instanceof MethodArgumentNotValidException) { 39 | bindingResult = ((MethodArgumentNotValidException) arg).getBindingResult(); 40 | } 41 | } 42 | if (bindingResult != null) { 43 | if (bindingResult.hasErrors()) { 44 | String errorInfo = bindingResult.getFieldError().getDefaultMessage(); 45 | return ResponseVoUtil.failResult(errorInfo); 46 | } 47 | } 48 | return joinPoint.proceed(); 49 | } 50 | } -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/config/LocaleConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lccf.config; 2 | 3 | import org.springframework.boot.bind.RelaxedPropertyResolver; 4 | import org.springframework.context.EnvironmentAware; 5 | import org.springframework.context.MessageSource; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.support.ReloadableResourceBundleMessageSource; 9 | import org.springframework.core.env.Environment; 10 | import org.springframework.web.context.request.RequestContextHolder; 11 | import org.springframework.web.context.request.ServletRequestAttributes; 12 | import org.springframework.web.servlet.LocaleResolver; 13 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 14 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 15 | import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; 16 | import org.springframework.web.servlet.i18n.SessionLocaleResolver; 17 | import org.springframework.web.servlet.support.RequestContext; 18 | import org.springframework.web.servlet.support.RequestContextUtils; 19 | 20 | import javax.servlet.http.HttpServletRequest; 21 | import java.util.Locale; 22 | 23 | @Configuration 24 | public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware { 25 | 26 | private RelaxedPropertyResolver propertyResolver; 27 | 28 | @Override 29 | public void setEnvironment(Environment environment) { 30 | this.propertyResolver = new RelaxedPropertyResolver(environment, "spring.messages."); 31 | } 32 | @Bean 33 | public LocaleResolver localeResolver() { 34 | SessionLocaleResolver localeResolver = new SessionLocaleResolver(); 35 | localeResolver.setDefaultLocale(new Locale("en")); 36 | return localeResolver; 37 | } 38 | 39 | 40 | @Bean 41 | public MessageSource messageSource() { 42 | ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 43 | messageSource.setBasename("classpath:/i18n/messages"); 44 | messageSource.setDefaultEncoding("UTF-8"); 45 | messageSource.setCacheSeconds(propertyResolver.getProperty("cache-seconds", Integer.class, -1)); 46 | return messageSource; 47 | } 48 | 49 | @Override 50 | public void addInterceptors(InterceptorRegistry registry) { 51 | 52 | LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); 53 | localeChangeInterceptor.setParamName("language"); 54 | 55 | registry.addInterceptor(localeChangeInterceptor); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/config/MappingConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lccf.config; 2 | 3 | import org.dozer.spring.DozerBeanMapperFactoryBean; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.io.Resource; 8 | 9 | @Configuration 10 | public class MappingConfiguration { 11 | 12 | @Bean 13 | public DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean(@Value("classpath*:mappings/*mappings.xml") Resource[] resources) throws Exception { 14 | final DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean = new DozerBeanMapperFactoryBean(); 15 | dozerBeanMapperFactoryBean.setMappingFiles(resources); 16 | return dozerBeanMapperFactoryBean; 17 | } 18 | } -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/config/ThymeleafConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lccf.config; 2 | 3 | import org.apache.commons.lang3.CharEncoding; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.context.MessageSource; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Description; 10 | import org.springframework.context.support.ReloadableResourceBundleMessageSource; 11 | import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; 12 | 13 | @Configuration 14 | public class ThymeleafConfiguration { 15 | 16 | private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class); 17 | 18 | @Bean 19 | @Description("Thymeleaf template resolver serving HTML 5 emails") 20 | public ClassLoaderTemplateResolver emailTemplateResolver() { 21 | ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver(); 22 | emailTemplateResolver.setPrefix("mails/"); 23 | emailTemplateResolver.setSuffix(".html"); 24 | emailTemplateResolver.setTemplateMode("HTML5"); 25 | emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8); 26 | emailTemplateResolver.setOrder(1); 27 | return emailTemplateResolver; 28 | } 29 | 30 | @Bean 31 | @Description("Spring mail message resolver") 32 | public MessageSource emailMessageSource() { 33 | log.info("loading non-reloadable mail messages resources"); 34 | ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 35 | messageSource.setBasename("classpath:/mails/messages/messages"); 36 | messageSource.setDefaultEncoding(CharEncoding.UTF_8); 37 | return messageSource; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/config/apidoc/SwaggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lccf.config.apidoc; 2 | 3 | import com.lccf.constants.Constants; 4 | import com.lccf.constants.LccfProperties; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Profile; 10 | import org.springframework.data.domain.Pageable; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 13 | import org.springframework.util.StopWatch; 14 | import springfox.documentation.builders.PathSelectors; 15 | import springfox.documentation.builders.RequestHandlerSelectors; 16 | import springfox.documentation.service.ApiInfo; 17 | import springfox.documentation.spi.DocumentationType; 18 | import springfox.documentation.spring.web.plugins.Docket; 19 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 20 | 21 | import java.util.Date; 22 | 23 | import static springfox.documentation.builders.PathSelectors.regex; 24 | 25 | 26 | @Configuration 27 | @EnableSwagger2 28 | @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) 29 | public class SwaggerConfiguration { 30 | 31 | private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class); 32 | 33 | public static final String DEFAULT_INCLUDE_PATTERN = "/api/.*"; 34 | 35 | /** 36 | * Swagger Springfox configuration. 37 | */ 38 | @Bean 39 | @Profile("!" + Constants.SPRING_PROFILE_FAST) 40 | public Docket swaggerSpringfoxDocket(LccfProperties properties) { 41 | log.debug("Starting Swagger"); 42 | StopWatch watch = new StopWatch(); 43 | watch.start(); 44 | ApiInfo apiInfo = new ApiInfo( 45 | properties.getSwagger().getTitle(), 46 | properties.getSwagger().getDescription(), 47 | properties.getSwagger().getVersion(), 48 | properties.getSwagger().getTermsOfServiceUrl(), 49 | properties.getSwagger().getContact(), 50 | properties.getSwagger().getLicense(), 51 | properties.getSwagger().getLicenseUrl()); 52 | 53 | Docket docket = new Docket(DocumentationType.SWAGGER_2) 54 | .apiInfo(apiInfo) 55 | .genericModelSubstitutes(ResponseEntity.class) 56 | .forCodeGeneration(true) 57 | .genericModelSubstitutes(ResponseEntity.class) 58 | .ignoredParameterTypes(Pageable.class) 59 | .directModelSubstitute(java.time.LocalDate.class, String.class) 60 | .directModelSubstitute(java.time.ZonedDateTime.class, Date.class) 61 | .directModelSubstitute(java.time.LocalDateTime.class, Date.class) 62 | .select() 63 | .apis(RequestHandlerSelectors.basePackage("com.lccf.controller")) 64 | .paths(PathSelectors.regex(DEFAULT_INCLUDE_PATTERN)) 65 | 66 | .build(); 67 | watch.stop(); 68 | log.debug("Started Swagger in {} ms", watch.getTotalTimeMillis()); 69 | return docket; 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/config/redis/CacheConfig.java: -------------------------------------------------------------------------------- 1 | package com.lccf.config.redis; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.lccf.util.Collections3; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.cache.CacheManager; 9 | import org.springframework.cache.annotation.CachingConfigurerSupport; 10 | import org.springframework.cache.annotation.EnableCaching; 11 | import org.springframework.cache.interceptor.KeyGenerator; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.data.redis.cache.RedisCacheManager; 15 | import org.springframework.data.redis.connection.RedisClusterConfiguration; 16 | import org.springframework.data.redis.connection.RedisConnectionFactory; 17 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 18 | import org.springframework.data.redis.core.RedisTemplate; 19 | import org.springframework.data.redis.core.StringRedisTemplate; 20 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 21 | 22 | import java.lang.reflect.Method; 23 | 24 | @Configuration 25 | @EnableCaching 26 | public class CacheConfig extends CachingConfigurerSupport { 27 | @Value("${spring.redis.cluster.nodes}") 28 | private String nodes; 29 | 30 | @Bean 31 | public KeyGenerator wiselyKeyGenerator(){ 32 | return new KeyGenerator() { 33 | @Override 34 | public Object generate(Object target, Method method, Object... params) { 35 | StringBuilder sb = new StringBuilder(); 36 | sb.append(target.getClass().getName()); 37 | sb.append(method.getName()); 38 | for (Object obj : params) { 39 | sb.append(obj.toString()); 40 | } 41 | return sb.toString(); 42 | } 43 | }; 44 | } 45 | 46 | @Bean 47 | 48 | public JedisConnectionFactory redisConnectionFactory() { 49 | JedisConnectionFactory factory = new JedisConnectionFactory( 50 | new RedisClusterConfiguration(Collections3.transStringToList(nodes,","))); 51 | return factory; 52 | } 53 | @Bean 54 | public CacheManager cacheManager(RedisTemplate redisTemplate) { 55 | RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); 56 | // Number of seconds before expiration. Defaults to unlimited (0) 57 | cacheManager.setDefaultExpiration(10); //设置key-value超时时间 58 | return cacheManager; 59 | } 60 | @Bean 61 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) { 62 | StringRedisTemplate template = new StringRedisTemplate(factory); 63 | setSerializer(template); //设置序列化工具,这样ReportBean不需要实现Serializable接口 64 | template.afterPropertiesSet(); 65 | return template; 66 | } 67 | private void setSerializer(StringRedisTemplate template) { 68 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); 69 | ObjectMapper om = new ObjectMapper(); 70 | om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 71 | om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 72 | jackson2JsonRedisSerializer.setObjectMapper(om); 73 | template.setValueSerializer(jackson2JsonRedisSerializer); 74 | } 75 | } -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/config/security/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.lccf.config.security; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.core.userdetails.UserDetailsService; 14 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 15 | import org.springframework.security.crypto.password.PasswordEncoder; 16 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 17 | 18 | import com.lccf.filter.JWTAuthenticationFilter; 19 | import com.lccf.filter.JWTLoginFilter; 20 | import com.lccf.service.security.CustomAccessDeniedHandler; 21 | 22 | @Configuration 23 | @EnableWebSecurity 24 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 25 | 26 | @Inject 27 | private UserDetailsService userDetailsService; 28 | 29 | @Bean 30 | public PasswordEncoder passwordEncoder() { 31 | return new BCryptPasswordEncoder(); 32 | } 33 | 34 | @Override 35 | protected void configure(HttpSecurity http) throws Exception { 36 | http.csrf().disable().authorizeRequests().antMatchers("/").permitAll().antMatchers(HttpMethod.POST, "/api/register").permitAll() 37 | .antMatchers(HttpMethod.GET,"/api/user/isExistsUserName").permitAll() 38 | .antMatchers(HttpMethod.GET,"/api/download").permitAll() 39 | .antMatchers(HttpMethod.GET,"/api/demo").permitAll() 40 | .antMatchers(HttpMethod.POST, "/login").permitAll().anyRequest().authenticated().and() 41 | .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class) 42 | .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).exceptionHandling() 43 | .accessDeniedHandler(new CustomAccessDeniedHandler()); 44 | 45 | } 46 | 47 | @Override 48 | public void configure(WebSecurity web) throws Exception { 49 | web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", 50 | "/webjars/**"); 51 | } 52 | 53 | @Override 54 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 55 | auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); 56 | } 57 | } -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/controller/EmailController.java: -------------------------------------------------------------------------------- 1 | package com.lccf.controller; 2 | 3 | import com.lccf.base.controller.BaseController; 4 | import com.lccf.constants.Constants; 5 | import com.lccf.domain.JltEmailTpl; 6 | import com.lccf.exception.BusinessException; 7 | import com.lccf.param.EmailReq; 8 | import com.lccf.service.mail.JltEmailService; 9 | import com.lccf.util.ResponseVo; 10 | import com.lccf.util.ResponseVoUtil; 11 | import java.util.Optional; 12 | 13 | import javax.annotation.Resource; 14 | 15 | import org.springframework.http.MediaType; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.validation.annotation.Validated; 18 | import org.springframework.web.bind.annotation.RequestBody; 19 | import org.springframework.web.bind.annotation.RequestMapping; 20 | import org.springframework.web.bind.annotation.RequestMethod; 21 | import org.springframework.web.bind.annotation.RestController; 22 | 23 | 24 | 25 | import io.swagger.annotations.ApiOperation; 26 | import io.swagger.annotations.ApiParam; 27 | 28 | /** 29 | * 发送邮件 30 | * @author lichangchao 31 | * @version 1.0.0 32 | * @date 2017/12/4 16:46 33 | * @see 34 | */ 35 | @RestController 36 | 37 | public class EmailController extends BaseController { 38 | @Resource 39 | JltEmailService jltEmailService; 40 | 41 | @RequestMapping(value = "/sendEmail", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 42 | @ApiOperation(value = "发送邮件", httpMethod = "POST", response = ResponseEntity.class) 43 | public ResponseVo sendEmail(@ApiParam(value = "邮件发送参数", required = true)@RequestBody @Validated EmailReq emailReq) { 44 | //限制频率 45 | String key = EMAIL_KEY+emailReq.getTo(); 46 | boolean apiFlag = this.apiFrequency(key,600L,5); 47 | if(!apiFlag){ 48 | throw new BusinessException("频率限制:10分钟内5封"); 49 | } 50 | 51 | //获取模板判断模板是否存在 52 | Optional jltEmailTplOptional = jltEmailService.getContentByCode(emailReq.getTplCode()); 53 | jltEmailTplOptional.orElseThrow(() ->new BusinessException("模板不存在")); 54 | 55 | //判断该应用邮件功能是否启用 56 | boolean isOpen = jltEmailService.isOpen(jltEmailTplOptional.get().getAppId()); 57 | if(!isOpen){ 58 | throw new BusinessException("该应用邮件功能被禁用"); 59 | } 60 | //发送邮件 61 | jltEmailService.sendEmail(emailReq, jltEmailTplOptional.get()); 62 | return ResponseVoUtil.successMsg("发送成功"); 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/controller/GraphqlController.java: -------------------------------------------------------------------------------- 1 | package com.lccf.controller; 2 | 3 | import static graphql.schema.GraphQLArgument.newArgument; 4 | import static graphql.schema.GraphQLObjectType.newObject; 5 | 6 | import java.util.Map; 7 | 8 | import javax.annotation.Resource; 9 | 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import com.fasterxml.jackson.core.JsonProcessingException; 15 | import com.fasterxml.jackson.databind.ObjectMapper; 16 | import com.lccf.base.controller.BaseController; 17 | import com.lccf.service.user.IUserService; 18 | 19 | import graphql.GraphQL; 20 | import graphql.Scalars; 21 | import graphql.schema.GraphQLFieldDefinition; 22 | import graphql.schema.GraphQLOutputType; 23 | import graphql.schema.GraphQLSchema; 24 | 25 | /** 26 | * 27 | * Graphql 列子 28 | * @author lichangchao 29 | * @version 1.0.0 30 | * @date 2017/10/31 11:21 31 | * @see 32 | */ 33 | @RestController 34 | public class GraphqlController extends BaseController { 35 | 36 | private static GraphQLOutputType userType; 37 | public static GraphQLSchema schema; 38 | @Resource 39 | IUserService userService; 40 | 41 | 42 | 43 | @GetMapping(value = "demo") 44 | public String demo(@RequestParam(value = "query", required = false) String query) 45 | throws JsonProcessingException { 46 | GraphSchema(); 47 | /* 48 | * Query example: 49 | * 50 | * String query1 = "{users(page:2,size:5,name:\"john\") {id,name}}"; 51 | * String query2 = "{user(id:6) {id,name}}"; 52 | * String query3 = "{user(id:6) {id,name},users(page:2,size:5,name:\"john\") {id,name}}" 53 | * ; 54 | */ 55 | query = "{user(userName:\"licc168\") {id,userName}}"; 56 | //Fetch the result with query string 57 | Map result = new GraphQL(schema).execute(query).getData(); 58 | 59 | return new ObjectMapper().writeValueAsString(result == null ? "Invaid query! \n Please correct the query and try again. : )" : result); 60 | } 61 | 62 | 63 | /** 64 | * Init the output type 65 | */ 66 | private void initOutputType() { 67 | 68 | userType = newObject().name("User") 69 | .field(GraphQLFieldDefinition.newFieldDefinition().name("id").type(Scalars.GraphQLLong).build()) 70 | .field(GraphQLFieldDefinition.newFieldDefinition().name("userName").type(Scalars.GraphQLString).build()) 71 | //.field(GraphQLFieldDefinition.newFieldDefinition().name("realName").type(Scalars.GraphQLString).build()) 72 | .build(); 73 | } 74 | 75 | /** 76 | * Check single user 77 | * 78 | */ 79 | private GraphQLFieldDefinition createUserField() { 80 | return GraphQLFieldDefinition.newFieldDefinition().name("user") 81 | .argument(newArgument().name("userName").type(Scalars.GraphQLString).build()).type(userType).dataFetcher(environment -> { 82 | // 获取查询参数 83 | String userName = environment.getArgument("userName"); 84 | 85 | 86 | // 执行查询, 这里随便用一些测试数据来说明问题 87 | return userService.getByUserName(userName); 88 | }).build(); 89 | } 90 | 91 | // /** 92 | // * Check multiple users 93 | // * 94 | // */ 95 | // private GraphQLFieldDefinition createUsersField() { 96 | // return GraphQLFieldDefinition.newFieldDefinition().name("users") 97 | // .argument(newArgument().name("page").type(Scalars.GraphQLInt).build()) 98 | // .argument(newArgument().name("size").type(Scalars.GraphQLInt).build()) 99 | // .argument(newArgument().name("name").type(Scalars.GraphQLString).build()).type(new GraphQLList(userType)) 100 | // .dataFetcher(environment -> { 101 | // // 获取查询参数 102 | // int page = environment.getArgument("page"); 103 | // int size = environment.getArgument("size"); 104 | // String name = environment.getArgument("name"); 105 | // 106 | // // 执行查询, 这里随便用一些测试数据来说明问题 107 | // List list = new ArrayList<>(size); 108 | // for (int i = 0; i < size; i++) { 109 | // User user = new User(); 110 | // user.setId(i); 111 | // user.setAge((short)i ); 112 | // user.setName("Name_" + i); 113 | // list.add(user); 114 | // } 115 | // return list; 116 | // }).build(); 117 | // } 118 | 119 | 120 | private void GraphSchema() { 121 | initOutputType(); 122 | schema = GraphQLSchema.newSchema().query(newObject() 123 | .name("GraphQuery") 124 | .field(createUserField()) 125 | .build()).build(); 126 | 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/controller/MenuController.java: -------------------------------------------------------------------------------- 1 | package com.lccf.controller; 2 | 3 | import java.util.List; 4 | 5 | import javax.annotation.Resource; 6 | 7 | import org.springframework.data.domain.Page; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestMethod; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import com.lccf.base.controller.BaseController; 16 | import com.lccf.domain.Menu; 17 | import com.lccf.service.menu.IMenuService; 18 | import com.lccf.service.menu.MenuParam; 19 | import com.lccf.service.menu.MenuVo; 20 | import com.lccf.service.menu.impl.MenuUtil; 21 | import com.lccf.util.ResponseVo; 22 | import com.lccf.util.ResponseVoUtil; 23 | 24 | import io.swagger.annotations.ApiOperation; 25 | import io.swagger.annotations.ApiParam; 26 | 27 | /** 28 | * @author lichangchao 29 | * @Time 2017-04-24 菜单管理 30 | */ 31 | 32 | @RestController 33 | public class MenuController extends BaseController { 34 | @Resource 35 | IMenuService menuService; 36 | 37 | @RequestMapping(value = "/menu/list", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 38 | @ApiOperation(value = "获取全部菜单列表", httpMethod = "GET", response = List.class) 39 | public ResponseVo list() { 40 | List menuList = menuService.findByDeleteFlagAndParentId(false, null); 41 | String menuJson = MenuUtil.transMenuListTOJson(menuList); 42 | return ResponseVoUtil.successResult("获取成功", menuJson); 43 | } 44 | 45 | @RequestMapping(value = "/menu/parentList", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 46 | @ApiOperation(value = "获取父级菜单列表-用于菜单页面下拉框", httpMethod = "GET", response = Menu.class) 47 | 48 | public ResponseVo parentList() { 49 | List menuList = menuService.findByDeleteFlagAndParentId(false, null); 50 | return ResponseVoUtil.successData(menuList); 51 | 52 | } 53 | 54 | @RequestMapping(value = "/menu/page", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 55 | @ApiOperation(value = "获取菜单列表-分页", httpMethod = "GET", response = Page.class) 56 | 57 | public ResponseVo page(@ApiParam(value = "用户参数", required = true) MenuParam menuParam) { 58 | Page menuPage = menuService.page(menuParam); 59 | return ResponseVoUtil.successData(menuPage); 60 | 61 | } 62 | 63 | @RequestMapping(value = "/menu/save", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 64 | @ApiOperation(value = "新增菜单信息", httpMethod = "POST", response = String.class) 65 | 66 | public ResponseVo save(@RequestBody @ApiParam(value = "用户参数", required = true) MenuParam menuParam) { 67 | menuService.save(menuParam); 68 | return ResponseVoUtil.successMsg("操作成功"); 69 | } 70 | 71 | @RequestMapping(value = "/menu/get/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) 72 | @ApiOperation(value = "新增菜单信息", httpMethod = "POST", response = Page.class) 73 | public ResponseVo getById(@ApiParam(value = "用户ID", required = true) @PathVariable Long id) { 74 | Menu menu = menuService.findOne(id); 75 | return ResponseVoUtil.successData(menu); 76 | 77 | } 78 | 79 | @RequestMapping(value = "/menu/delete/{id}", method = RequestMethod.DELETE) 80 | @ApiOperation(value = "删除用户", httpMethod = "DELETE", response = String.class, notes = "") 81 | public ResponseVo deleteById(@ApiParam(value = "用户ID", required = true) @PathVariable Long id) { 82 | menuService.updateDeleteFlagById(id); 83 | return ResponseVoUtil.successMsg("操作成功"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.lccf.controller; 2 | 3 | import javax.annotation.Resource; 4 | import javax.validation.Valid; 5 | 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import com.lccf.base.controller.BaseController; 16 | import com.lccf.domain.User; 17 | import com.lccf.service.user.IUserService; 18 | import com.lccf.service.user.UserParam; 19 | import com.lccf.util.ResponseVo; 20 | import com.lccf.util.ResponseVoUtil; 21 | 22 | import io.swagger.annotations.ApiOperation; 23 | import io.swagger.annotations.ApiParam; 24 | 25 | @RestController 26 | public class UserController extends BaseController { 27 | 28 | @Resource 29 | IUserService userService; 30 | 31 | /** 32 | * 注册用户 33 | * 34 | * @param userParam 35 | * @see com.lccf.service.user.UserParam 36 | */ 37 | @RequestMapping(value = "/register", method = RequestMethod.POST) 38 | @ApiOperation(value = "注册用户", httpMethod = "POST", response = String.class, notes = "注册接口(用户名/邮箱/密码)") 39 | public ResponseVo register(@Valid @RequestBody @ApiParam(value = "用户参数", required = true) UserParam userParam) { 40 | userService.register(userParam); 41 | return ResponseVoUtil.successMsg("注册成功"); 42 | } 43 | 44 | /** 45 | * 验证用户名是否存在 46 | * 47 | * @see com.lccf.service.user.UserParam 48 | */ 49 | @RequestMapping(value = "/user/isExistsUserName", method = RequestMethod.GET) 50 | @ApiOperation(value = "验证用户名是否存在", httpMethod = "GET", response = String.class, notes = "判断用户名是否存在") 51 | public ResponseVo isExistsUserName(@ApiParam(value = "用户名", required = true) @RequestParam String userName) { 52 | User user = userService.getByUserName(userName); 53 | if (user == null) { 54 | return ResponseVoUtil.failResult(null); 55 | } 56 | return ResponseVoUtil.successData(null); 57 | } 58 | 59 | /** 60 | * 获取用户数据-分页 61 | * 62 | * @param userParam 63 | * @return 64 | */ 65 | @RequestMapping(value = "/user/page", method = RequestMethod.GET) 66 | @ApiOperation(value = "获取用户数据", httpMethod = "GET", response = Page.class, notes = "") 67 | public ResponseVo page(@ApiParam(value = "用户参数", required = true) UserParam userParam) { 68 | Page userPage = userService.page(userParam); 69 | return ResponseVoUtil.successData(userPage); 70 | } 71 | 72 | @RequestMapping(value = "/user/delete/{id}", method = RequestMethod.DELETE) 73 | @ApiOperation(value = "删除用户", httpMethod = "DELETE", response = String.class, notes = "") 74 | public ResponseVo deleteById(@ApiParam(value = "用户ID", required = true) @PathVariable Long id) { 75 | userService.deleteById(id); 76 | return ResponseVoUtil.successMsg("用户成功"); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/domain/JltEmailLog.java: -------------------------------------------------------------------------------- 1 | package com.lccf.domain; 2 | 3 | import java.util.Date; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.Table; 11 | import javax.persistence.Temporal; 12 | import javax.persistence.TemporalType; 13 | 14 | import org.springframework.data.annotation.CreatedDate; 15 | import org.springframework.data.annotation.LastModifiedDate; 16 | 17 | import lombok.Data; 18 | 19 | /** 20 | * 邮箱日志 21 | * @author lichangchao 22 | * @version 1.0.0 23 | * @date 2017/12/4 16:30 24 | * @see 25 | */ 26 | @Data 27 | @Entity 28 | @Table(name = "jlt_email_log") 29 | public class JltEmailLog { 30 | @Id 31 | @GeneratedValue(strategy = GenerationType.AUTO) 32 | private Long id; 33 | //应用ID 34 | @Column(name = "app_id",nullable = false) 35 | private Integer appId; 36 | 37 | //邮件主题 38 | @Column(name = "subject",nullable = false) 39 | private String subject; 40 | 41 | //邮件内容 42 | @Column(name = "content",nullable = false,length = 2000) 43 | private String content; 44 | 45 | 46 | //收件人 47 | @Column(name = "recipient",nullable = false) 48 | private String recipient; 49 | 50 | //状态 51 | @Column(name = "status") 52 | private Integer status; 53 | 54 | 55 | @Column(name = "create_time") 56 | @CreatedDate 57 | private Date createTime; 58 | 59 | @Temporal(TemporalType.TIMESTAMP) 60 | @Column(name = "update_time") 61 | @LastModifiedDate 62 | private Date updateTime; 63 | 64 | @Column(name = "delete_flag") 65 | private Boolean deleteFlag; 66 | } 67 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/domain/JltEmailTpl.java: -------------------------------------------------------------------------------- 1 | package com.lccf.domain; 2 | 3 | import java.util.Date; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.Table; 11 | import javax.persistence.Temporal; 12 | import javax.persistence.TemporalType; 13 | 14 | import org.springframework.data.annotation.CreatedDate; 15 | import org.springframework.data.annotation.LastModifiedDate; 16 | 17 | import lombok.Data; 18 | 19 | /** 20 | * 邮箱模板 21 | * @author lichangchao 22 | * @version 1.0.0 23 | * @date 2017/12/4 16:30 24 | * @see 25 | */ 26 | @Data 27 | @Entity 28 | @Table(name = "jlt_email_tpl") 29 | public class JltEmailTpl { 30 | @Id 31 | @GeneratedValue(strategy = GenerationType.AUTO) 32 | private Long id; 33 | //应用ID 34 | @Column(name = "app_id",nullable = false) 35 | private Integer appId; 36 | 37 | //模板编号 38 | @Column(name = "code",nullable = false) 39 | private String code; 40 | 41 | //模板内容 42 | @Column(name = "content",nullable = false,length = 2000) 43 | private String content; 44 | //备注 45 | @Column(name = "remark",length = 200) 46 | private String remark; 47 | 48 | @Column(name = "create_time") 49 | @CreatedDate 50 | private Date createTime; 51 | 52 | @Temporal(TemporalType.TIMESTAMP) 53 | @Column(name = "update_time") 54 | @LastModifiedDate 55 | 56 | private Date updateTime; 57 | 58 | 59 | 60 | @Column(name = "delete_flag") 61 | private Boolean deleteFlag; 62 | } 63 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/domain/Menu.java: -------------------------------------------------------------------------------- 1 | package com.lccf.domain; 2 | 3 | import java.util.Date; 4 | import java.io.Serializable; 5 | import java.util.Set; 6 | import javax.persistence.CascadeType; 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.FetchType; 10 | import javax.persistence.GeneratedValue; 11 | import javax.persistence.GenerationType; 12 | import javax.persistence.Id; 13 | import javax.persistence.JoinColumn; 14 | import javax.persistence.OneToMany; 15 | import javax.persistence.Table; 16 | import javax.persistence.Temporal; 17 | import javax.persistence.TemporalType; 18 | import lombok.Data; 19 | import org.springframework.data.annotation.CreatedDate; 20 | import org.springframework.data.annotation.LastModifiedDate; 21 | 22 | /** 23 | * @author lichangchao 24 | * @Time 2017 -04-24 15:05:56 25 | */ 26 | @Data 27 | @Entity 28 | @Table(name = "menu") 29 | public class Menu implements Serializable { 30 | @Id 31 | @GeneratedValue(strategy = GenerationType.AUTO) 32 | private Long id; 33 | @Column(name = "path") 34 | private String path; 35 | @Column(name = "title", nullable = true) 36 | private String title; 37 | @Column(name = "icon", nullable = true) 38 | private String icon; 39 | @Column(name = "selected") 40 | private Boolean selected; 41 | @Column(name = "expanded") 42 | private Boolean expanded; 43 | @Column(name = "delete_flag") 44 | private Boolean deleteFlag; 45 | @Column(name="parent_id") 46 | private Long parentId; 47 | @Column(name="order_num") 48 | private Integer orderNum; 49 | 50 | /* @OneToMany(mappedBy = "menu",cascade = CascadeType.ALL) 51 | @JoinTable( 52 | name = "menu", 53 | joinColumns = {@JoinColumn(name = "parent_id", referencedColumnName = "id")}) 54 | 55 | @ManyToOne(cascade = CascadeType.ALL, optional = true) 56 | @JoinColumn(referencedColumnName = "id", name = "parent_id", insertable = false, updatable = false)*/ 57 | 58 | @OneToMany(fetch= FetchType.EAGER,cascade= CascadeType.ALL) 59 | @JoinColumn(name="parent_id") 60 | 61 | private Set children; 62 | /** 63 | * 创建时间 64 | */ 65 | @Column(name = "create_time") 66 | @CreatedDate 67 | private Date createTime; 68 | /** 69 | * 修改时间 70 | */ 71 | @Column(name = "update_time") 72 | @LastModifiedDate 73 | private Date updateTime; 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.lccf.domain; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.GenerationType; 10 | import javax.persistence.Id; 11 | import javax.persistence.Table; 12 | import javax.persistence.Temporal; 13 | import javax.persistence.TemporalType; 14 | import javax.validation.constraints.NotNull; 15 | import javax.validation.constraints.Size; 16 | 17 | import org.springframework.data.annotation.CreatedDate; 18 | import org.springframework.data.annotation.LastModifiedDate; 19 | 20 | import com.fasterxml.jackson.annotation.JsonIgnore; 21 | 22 | import lombok.Data; 23 | 24 | 25 | /** 26 | * @author lichangchao 27 | * @Time 2017 -03-20 15:05:56 28 | */ 29 | @Entity 30 | @Table(name = "user") 31 | @Data 32 | public class User implements Serializable { 33 | 34 | @Id 35 | @GeneratedValue(strategy = GenerationType.AUTO) 36 | private Long id; 37 | 38 | @NotNull 39 | @Column(name = "user_name",length = 50,unique = true,nullable = false) 40 | private String userName; 41 | 42 | @Column(name = "real_name",length = 100) 43 | private String realName; 44 | 45 | @Column(name = "email",length = 100) 46 | private String email; 47 | 48 | @Column(name = "mobile",length = 100) 49 | private String mobile; 50 | 51 | @JsonIgnore 52 | 53 | @Column(name = "password",length = 60) 54 | private String password; 55 | 56 | 57 | @Column(name = "status") 58 | private Integer status; 59 | 60 | @Size(max = 20) 61 | @Column(name = "activation_key", length = 20) 62 | @JsonIgnore 63 | private String activationKey; 64 | 65 | @Column(name = "delete_flag" ) 66 | private Boolean deleteFlag; 67 | /** 68 | * 创建时间 69 | */ 70 | @Column(name = "create_time") 71 | @CreatedDate 72 | private Date createTime; 73 | /** 74 | * 修改时间 75 | */ 76 | @Temporal(TemporalType.TIMESTAMP) 77 | @Column(name = "update_time") 78 | @LastModifiedDate 79 | 80 | private Date updateTime; 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.lccf.exception; 2 | 3 | /** 4 | * 自定义业务异常 5 | * 6 | * @author lichangchao 7 | * @version 1.0.0 8 | * @date 2017/12/4 18:33 9 | * @see 10 | */ 11 | public class BusinessException extends RuntimeException { 12 | public BusinessException(String message) { 13 | super(message); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/exception/ExceptionConst.java: -------------------------------------------------------------------------------- 1 | package com.lccf.exception; 2 | 3 | /** 4 | * 错误信息描述 5 | * 6 | * @author lichangchao 7 | * @version 1.0.0 8 | * @date 2017/8/3 16:39 9 | * @see 10 | */ 11 | class ExceptionConst { 12 | static final String USER_NOT_ACTIVATE = "用户未激活"; 13 | static final String USER_NOT_FOUND = "用户名不存在"; 14 | static final String SYS_EXCEPTION = "系统异常"; 15 | } 16 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/exception/ExceptionTranslator.java: -------------------------------------------------------------------------------- 1 | package com.lccf.exception; 2 | 3 | import com.lccf.util.ResponseVo; 4 | import com.lccf.util.ResponseVoUtil; 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 12 | import org.springframework.web.bind.annotation.ControllerAdvice; 13 | import org.springframework.web.bind.annotation.ExceptionHandler; 14 | import org.springframework.web.bind.annotation.ResponseBody; 15 | 16 | import com.lccf.service.security.UserNotActivatedException; 17 | 18 | @ControllerAdvice 19 | public class ExceptionTranslator { 20 | protected final Logger logger = LoggerFactory.getLogger(this.getClass()); 21 | 22 | @ExceptionHandler(value = Exception.class) 23 | @ResponseBody 24 | public ResponseVo loginException(Exception e, HttpServletRequest request) { 25 | e.printStackTrace(); 26 | logger.error(e.getMessage()); 27 | String exceptionMsg = getException(e); 28 | return ResponseVoUtil.failResult(exceptionMsg); 29 | } 30 | 31 | /** 32 | * 获取错误信息 33 | * 34 | * @param exception 35 | * @return 36 | */ 37 | 38 | private String getException(Exception exception) { 39 | if (exception instanceof UserNotActivatedException) { 40 | return ExceptionConst.USER_NOT_ACTIVATE; 41 | } else if (exception instanceof UsernameNotFoundException) { 42 | return ExceptionConst.USER_NOT_FOUND; 43 | } else { 44 | return ExceptionConst.SYS_EXCEPTION; 45 | } 46 | 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/filter/JWTAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.lccf.filter; 2 | 3 | import com.lccf.service.security.TokenAuthenticationService; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.context.SecurityContextHolder; 6 | import org.springframework.web.filter.GenericFilterBean; 7 | 8 | import javax.servlet.FilterChain; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletRequest; 13 | import java.io.IOException; 14 | 15 | public class JWTAuthenticationFilter extends GenericFilterBean { 16 | 17 | @Override 18 | public void doFilter(ServletRequest request, 19 | ServletResponse response, 20 | FilterChain filterChain) throws IOException, ServletException { 21 | Authentication authentication = TokenAuthenticationService 22 | .getAuthentication((HttpServletRequest) request); 23 | SecurityContextHolder.getContext() 24 | .setAuthentication(authentication); 25 | filterChain.doFilter(request, response); 26 | } 27 | } -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/filter/JWTLoginFilter.java: -------------------------------------------------------------------------------- 1 | package com.lccf.filter; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; 15 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 16 | 17 | import com.lccf.service.security.TokenAuthenticationService; 18 | 19 | public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { 20 | 21 | public JWTLoginFilter(String url, AuthenticationManager authManager) { 22 | super(new AntPathRequestMatcher(url)); 23 | setAuthenticationManager(authManager); 24 | } 25 | 26 | @Override 27 | public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) { 28 | String userName = req.getParameter("userName"); 29 | String password = req.getParameter("password"); 30 | UserParam creds = new UserParam(); 31 | creds.setUserName(userName); 32 | creds.setPassword(password); 33 | Authentication authentication = getAuthenticationManager() 34 | .authenticate(new UsernamePasswordAuthenticationToken(creds.getUserName(), creds.getPassword(), Collections.emptyList())); 35 | 36 | return authentication; 37 | } 38 | 39 | @Override 40 | protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) 41 | throws IOException, ServletException { 42 | 43 | TokenAuthenticationService.addAuthentication(res, auth.getName()); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/filter/UserParam.java: -------------------------------------------------------------------------------- 1 | package com.lccf.filter; 2 | 3 | /** 4 | * @author lichangchao 5 | * @Time 2017 -03-28 10:12:42 6 | */ 7 | public class UserParam { 8 | private String userName; 9 | private String password; 10 | 11 | public String getUserName() { 12 | return userName; 13 | } 14 | 15 | public void setUserName(String userName) { 16 | this.userName = userName; 17 | } 18 | 19 | public String getPassword() { 20 | return password; 21 | } 22 | 23 | public void setPassword(String password) { 24 | this.password = password; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/param/EmailReq.java: -------------------------------------------------------------------------------- 1 | package com.lccf.param; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import javax.validation.constraints.Pattern; 5 | 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | 10 | /** 11 | * 发送邮件请求参数 12 | * @author lichangchao 13 | * @version 1.0.0 14 | * @date 2017/12/4 16:48 15 | * @see 16 | */ 17 | @Data 18 | @ApiModel(value = "邮件参数") 19 | public class EmailReq { 20 | @NotNull(message = "邮件接受者不能为空") 21 | @Pattern(regexp="[\\w-.]+@[\\w-]+(.[\\w_-]+)+",message = "邮箱格式不正确") 22 | @ApiModelProperty(name = "to",value="邮件接受者") 23 | String to; 24 | 25 | 26 | @NotNull(message = "模板编码不能为空") 27 | @ApiModelProperty(name = "tplCode",value="模板编码") 28 | String tplCode; 29 | 30 | @ApiModelProperty(name = "tplParam",value="模板参数") 31 | String tplParam; 32 | 33 | @ApiModelProperty(name = "subject",value="主题") 34 | String subject; 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/repository/JltEmialLogRepository.java: -------------------------------------------------------------------------------- 1 | package com.lccf.repository; 2 | 3 | import com.lccf.domain.JltEmailLog; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | 7 | /** 8 | * 9 | * @author lichangchao 10 | * @version 1.0.0 11 | * @date 2017/12/4 17:02 12 | * @see 13 | */ 14 | public interface JltEmialLogRepository extends JpaRepository { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/repository/JltEmialTplRepository.java: -------------------------------------------------------------------------------- 1 | package com.lccf.repository; 2 | 3 | import com.lccf.domain.JltEmailTpl; 4 | import java.util.List; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | 8 | /** 9 | * 10 | * @author lichangchao 11 | * @version 1.0.0 12 | * @date 2017/12/4 17:02 13 | * @see 14 | */ 15 | public interface JltEmialTplRepository extends JpaRepository { 16 | List findByCodeAndDeleteFlag(String code, boolean deleteFlag); 17 | } 18 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/repository/MenuRepostiory.java: -------------------------------------------------------------------------------- 1 | package com.lccf.repository; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.cache.annotation.CacheConfig; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | import com.lccf.domain.Menu; 9 | @CacheConfig 10 | public interface MenuRepostiory extends JpaRepository { 11 | 12 | List findByDeleteFlagAndParentId(Boolean deleteFlag,Long parentId); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.lccf.repository; 2 | 3 | import com.lccf.domain.User; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | public interface UserRepository extends JpaRepository { 12 | List findByUserName(String userName); 13 | 14 | Optional findOneByUserName(String username); 15 | 16 | Page findAllByUserName(String username, Pageable pageable); 17 | } 18 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/mail/JltEmailService.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.mail; 2 | 3 | import com.lccf.domain.JltEmailTpl; 4 | import com.lccf.exception.BusinessException; 5 | import com.lccf.param.EmailReq; 6 | import java.util.Optional; 7 | 8 | 9 | /** 10 | * 11 | * @author lichangchao 12 | * @version 1.0.0 13 | * @date 2017/12/4 17:05 14 | * @see 15 | */ 16 | public interface JltEmailService { 17 | /** 18 | * 根据模板编号查询内容 19 | */ 20 | Optional getContentByCode(String code); 21 | boolean isOpen(Integer appId); 22 | void sendEmail(EmailReq req, JltEmailTpl tpl) throws BusinessException; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/mail/MailService.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.mail; 2 | 3 | import javax.annotation.Resource; 4 | import javax.inject.Inject; 5 | import javax.mail.internet.MimeMessage; 6 | 7 | import org.apache.commons.codec.CharEncoding; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | import org.springframework.mail.javamail.JavaMailSender; 11 | import org.springframework.mail.javamail.MimeMessageHelper; 12 | import org.springframework.scheduling.annotation.Async; 13 | import org.springframework.stereotype.Service; 14 | 15 | import com.lccf.constants.LccfProperties; 16 | 17 | 18 | @Service 19 | public class MailService { 20 | 21 | private static final Logger LOGGER = LogManager.getLogger(); 22 | 23 | @Resource 24 | private LccfProperties lccfProperties; 25 | 26 | @Inject 27 | private JavaMailSender javaMailSender; 28 | 29 | @Async 30 | public boolean sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) { 31 | boolean flag = true; 32 | LOGGER.info("发送邮件[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}", isMultipart, isHtml, to, subject, 33 | content); 34 | MimeMessage mimeMessage = javaMailSender.createMimeMessage(); 35 | try { 36 | MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, CharEncoding.UTF_8); 37 | message.setTo(to); 38 | message.setFrom(lccfProperties.getMail().getFrom()); 39 | message.setSubject(subject); 40 | message.setText(content, isHtml); 41 | javaMailSender.send(mimeMessage); 42 | LOGGER.debug("发送邮件 to User '{}'", to); 43 | } catch (Exception e) { 44 | LOGGER.warn("E-mail could not be sent to user '{}', exception is: {}", to, e.getMessage()); 45 | flag = false; 46 | } 47 | return flag; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/mail/impl/JltEmailServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.mail.impl; 2 | 3 | import com.lccf.domain.JltEmailLog; 4 | import com.lccf.domain.JltEmailTpl; 5 | import com.lccf.enums.StatusEmun; 6 | import com.lccf.param.EmailReq; 7 | import com.lccf.repository.JltEmialLogRepository; 8 | import com.lccf.repository.JltEmialTplRepository; 9 | import com.lccf.service.mail.JltEmailService; 10 | import com.lccf.service.mail.MailService; 11 | import com.lccf.util.StringUtil; 12 | import java.util.Date; 13 | import java.util.List; 14 | import java.util.Optional; 15 | 16 | import javax.annotation.Resource; 17 | 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.transaction.annotation.Transactional; 20 | import org.springframework.util.CollectionUtils; 21 | 22 | 23 | 24 | /** 25 | * 26 | * @author lichangchao 27 | * @version 1.0.0 28 | * @date 2017/12/4 17:08 29 | * @see 30 | */ 31 | @Service 32 | @Transactional 33 | public class JltEmailServiceImpl implements JltEmailService { 34 | @Resource 35 | JltEmialTplRepository emialTplRepository; 36 | @Resource 37 | JltEmialLogRepository emialLogRepository; 38 | 39 | 40 | 41 | @Resource 42 | MailService mailService; 43 | @Override 44 | public Optional getContentByCode(String code) { 45 | List list = emialTplRepository.findByCodeAndDeleteFlag(code,false); 46 | if(CollectionUtils.isEmpty(list))return Optional.empty(); 47 | return Optional.of(list.get(0)); 48 | } 49 | 50 | @Override 51 | public boolean isOpen(Integer appId) { 52 | //TODO 预留业务 53 | return true; 54 | } 55 | 56 | @Override 57 | public void sendEmail(EmailReq req, JltEmailTpl tpl) { 58 | String content = StringUtil.transContent(tpl.getContent(),req.getTplParam()); 59 | 60 | boolean flag = mailService.sendEmail(req.getTo(),req.getSubject(),content,false,true); 61 | JltEmailLog jltEmailLog = new JltEmailLog(); 62 | jltEmailLog.setAppId(tpl.getAppId()); 63 | jltEmailLog.setContent(content); 64 | jltEmailLog.setDeleteFlag(false); 65 | jltEmailLog.setRecipient(req.getTo()); 66 | jltEmailLog.setSubject(req.getSubject()); 67 | jltEmailLog.setCreateTime(new Date()); 68 | jltEmailLog.setStatus(flag? StatusEmun.SUCCESS.getCode():StatusEmun.FAIL.getCode()); 69 | emialLogRepository.save(jltEmailLog); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/menu/IMenuService.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.menu; 2 | 3 | import com.lccf.base.service.IBaseService; 4 | import com.lccf.domain.Menu; 5 | import org.springframework.data.domain.Page; 6 | import java.util.List; 7 | 8 | 9 | public interface IMenuService extends IBaseService { 10 | public List findByDeleteFlagAndParentId(Boolean deleteFlag,Long parentId); 11 | 12 | /** 13 | * 查询菜单列表 14 | * @param menuParam 15 | * @return 16 | */ 17 | Page page(MenuParam menuParam); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/menu/MenuParam.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.menu; 2 | 3 | import com.lccf.base.service.PageParam; 4 | import java.util.Date; 5 | import lombok.Data; 6 | /** 7 | * @author lichangchao 8 | * @date 2017 -05-02 20:47:49 9 | */ 10 | @Data 11 | public class MenuParam extends PageParam { 12 | private String path; 13 | private String title; 14 | private String icon; 15 | private Boolean selected = false; 16 | private Boolean expanded = false; 17 | private Boolean deleteFlag = false;; 18 | private Long parentId; 19 | private Integer orderNum; 20 | 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/menu/MenuVo.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.menu; 2 | 3 | 4 | import com.lccf.domain.Menu; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class MenuVo extends Menu{ 9 | private Long id; 10 | private String path; 11 | private String title; 12 | private String icon; 13 | private Boolean selected; 14 | private Boolean expanded; 15 | private Boolean deleteFlag; 16 | private Long parentId; 17 | private Integer orderNum; 18 | private String parentTitle; 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/menu/impl/MenuServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.menu.impl; 2 | 3 | import com.lccf.base.service.impl.BaseServiceImpl; 4 | import com.lccf.domain.Menu; 5 | import com.lccf.repository.MenuRepostiory; 6 | import com.lccf.service.menu.IMenuService; 7 | import com.lccf.service.menu.MenuParam; 8 | import com.lccf.service.menu.MenuVo; 9 | import com.lccf.util.BeanMapper; 10 | import org.springframework.core.convert.converter.Converter; 11 | import org.springframework.data.domain.Example; 12 | import org.springframework.data.domain.Page; 13 | import org.springframework.data.domain.Pageable; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.transaction.annotation.Transactional; 16 | 17 | import javax.annotation.Resource; 18 | import java.util.List; 19 | 20 | @Service 21 | @Transactional 22 | public class MenuServiceImpl extends BaseServiceImpl implements IMenuService { 23 | @Resource 24 | MenuRepostiory menuRepostiory; 25 | 26 | @Override 27 | public List findByDeleteFlagAndParentId(Boolean deleteFlag, Long parentId) { 28 | List menuList = menuRepostiory.findByDeleteFlagAndParentId(deleteFlag, parentId); 29 | return menuList; 30 | } 31 | 32 | 33 | @Override 34 | public Page page(MenuParam menuParam) { 35 | Pageable pageable = menuParam.transPageRequest(); 36 | Menu menu = BeanMapper.map(menuParam, Menu.class); 37 | menu.setDeleteFlag(false); 38 | Page menuPage = menuRepostiory.findAll(Example.of(menu), pageable); 39 | Page menuVoPage = this.pageDtoToPageVo(menuPage); 40 | return menuVoPage; 41 | } 42 | 43 | public Page pageDtoToPageVo(Page menuPage) { 44 | return menuPage.map(new Converter() { 45 | @Override 46 | public MenuVo convert(Menu t) { 47 | MenuVo vo = BeanMapper.map(t, MenuVo.class); 48 | Long parentId = vo.getParentId(); 49 | if(parentId==null){ 50 | vo.setParentTitle(""); 51 | }else { 52 | Menu parentMenu = menuRepostiory.findOne(vo.getParentId()); 53 | vo.setParentTitle(parentMenu.getTitle()); 54 | } 55 | return vo; 56 | } 57 | }); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/menu/impl/MenuUtil.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.menu.impl; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | 6 | import com.lccf.domain.Menu; 7 | import java.util.stream.Collectors; 8 | 9 | public class MenuUtil { 10 | public static String transMenuListTOJson(List list) { 11 | StringBuffer stringBuffer = new StringBuffer(); 12 | stringBuffer.append("[{'path': 'pages', 'children': ["); 13 | list.forEach(menu -> { 14 | stringBuffer 15 | .append("{'path':'" + menu.getPath() + "',").append("'data':{ 'menu':{'title':'" + menu.getTitle() + "','icon':'" 16 | + menu.getIcon() + "','selected':" + menu.getSelected()) 17 | .append(",'expanded':" + menu.getSelected() + ",'order':" + menu.getOrderNum()); 18 | if (menu.getChildren().size() == 0) { 19 | stringBuffer.append("}}},"); 20 | } else { 21 | stringBuffer.append("}},'children': ["); 22 | Set childList = menu.getChildren().stream().filter(child->!child.getDeleteFlag()).collect( 23 | Collectors.toSet()); 24 | int index = 1; 25 | for (Menu child : childList) { 26 | String s = "'}}},"; 27 | if (index == childList.size()) { 28 | s = "'}}}"; 29 | } 30 | stringBuffer.append( 31 | "{'path':'" + child.getPath() + "','data':{'menu':{'title':'" + child 32 | .getTitle()).append(s); 33 | index++; 34 | 35 | } 36 | ; 37 | stringBuffer.append("]},"); 38 | } 39 | }); 40 | String menus = stringBuffer.substring(0, stringBuffer.length() - 1) + "]}]"; 41 | return menus; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/security/CustomAccessDeniedHandler.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.security; 2 | 3 | import org.springframework.security.access.AccessDeniedException; 4 | import org.springframework.security.web.access.AccessDeniedHandler; 5 | import org.springframework.security.web.access.AccessDeniedHandlerImpl; 6 | import org.springframework.security.web.csrf.CsrfException; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.Cookie; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | /** 15 | * An implementation of AccessDeniedHandler by wrapping the AccessDeniedHandlerImpl. 16 | * 17 | * In addition to sending a 403 (SC_FORBIDDEN) HTTP error code, it will remove the invalid CSRF cookie from the browser 18 | * side when a CsrfException occurs. In this way the browser side application, e.g. JavaScript code, can 19 | * distinguish the CsrfException from other AccessDeniedExceptions and perform more specific operations. For instance, 20 | * send a GET HTTP method to obtain a new CSRF token. 21 | * 22 | * @see AccessDeniedHandlerImpl 23 | */ 24 | public class CustomAccessDeniedHandler implements AccessDeniedHandler { 25 | 26 | private AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl(); 27 | 28 | public void handle(HttpServletRequest request, HttpServletResponse response, 29 | AccessDeniedException accessDeniedException) throws IOException, ServletException { 30 | 31 | if (accessDeniedException instanceof CsrfException && !response.isCommitted()) { 32 | // Remove the session cookie so that client knows it's time to obtain a new CSRF token 33 | String pCookieName = "CSRF-TOKEN"; 34 | Cookie cookie = new Cookie(pCookieName, ""); 35 | cookie.setMaxAge(0); 36 | cookie.setHttpOnly(false); 37 | cookie.setPath("/"); 38 | response.addCookie(cookie); 39 | } 40 | 41 | accessDeniedHandlerImpl.handle(request, response, accessDeniedException); 42 | } 43 | 44 | /** 45 | * The error page to use. Must begin with a "/" and is interpreted relative to the current context root. 46 | * 47 | * @param errorPage the dispatcher path to display 48 | * 49 | * @throws IllegalArgumentException if the argument doesn't comply with the above limitations 50 | * @see AccessDeniedHandlerImpl#setErrorPage(String) 51 | */ 52 | public void setErrorPage(String errorPage) { 53 | accessDeniedHandlerImpl.setErrorPage(errorPage); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/security/TokenAuthenticationService.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.security; 2 | 3 | import com.lccf.domain.User; 4 | import com.lccf.repository.UserRepository; 5 | import com.lccf.util.CookieUtil; 6 | import io.jsonwebtoken.Jwts; 7 | import io.jsonwebtoken.MalformedJwtException; 8 | import io.jsonwebtoken.SignatureAlgorithm; 9 | import org.springframework.security 10 | .authentication.UsernamePasswordAuthenticationToken; 11 | import org.springframework.security.core.Authentication; 12 | 13 | import javax.inject.Inject; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.util.Date; 17 | import java.util.Optional; 18 | 19 | import static java.util.Collections.emptyList; 20 | 21 | public class TokenAuthenticationService { 22 | static final long EXPIRATIONTIME = 864_000_000; // 10 days 23 | static final String SECRET = "XXxLCcFHHh"; 24 | static final String TOKEN_PREFIX = "Bearer"; 25 | static final String HEADER_STRING = "Authorization"; 26 | static final String jwtTokenCookieName = "JWT-TOKEN"; 27 | 28 | public static void addAuthentication(HttpServletResponse res, String username) { 29 | String JWT = Jwts.builder() 30 | .setSubject(username) 31 | .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME)) 32 | .signWith(SignatureAlgorithm.HS512, SECRET) 33 | .compact(); 34 | CookieUtil.create(res, jwtTokenCookieName, JWT, false, -1, null); 35 | } 36 | 37 | public static Authentication getAuthentication(HttpServletRequest request) throws MalformedJwtException { 38 | String token = CookieUtil.getValue(request, jwtTokenCookieName); 39 | if (token != null) { 40 | String user = Jwts.parser() 41 | .setSigningKey(SECRET) 42 | .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) 43 | .getBody() 44 | .getSubject(); 45 | return user != null ? 46 | new UsernamePasswordAuthenticationToken(user, null, emptyList()) : 47 | null; 48 | } 49 | return null; 50 | } 51 | } -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/security/UserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.security; 2 | 3 | import com.lccf.domain.User; 4 | import com.lccf.enums.EUserStatus; 5 | import com.lccf.repository.UserRepository; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.security.core.GrantedAuthority; 9 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.transaction.annotation.Transactional; 14 | 15 | import javax.inject.Inject; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Optional; 19 | 20 | 21 | @Component("userDetailsService") 22 | public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService { 23 | 24 | private final Logger log = LoggerFactory.getLogger(UserDetailsService.class); 25 | 26 | @Inject 27 | private UserRepository userRepository; 28 | 29 | @Override 30 | @Transactional 31 | public UserDetails loadUserByUsername(final String login) { 32 | log.debug("Authenticating {}", login); 33 | String lowercaseLogin = login.toLowerCase(); 34 | Optional userFromDatabase = userRepository.findOneByUserName(lowercaseLogin); 35 | return userFromDatabase.map(user -> { 36 | if (user.getStatus() == EUserStatus.ACTIVITED.getKey()) { 37 | throw new UserNotActivatedException("用户名 " + lowercaseLogin + " 未激活"); 38 | } 39 | List grantedAuthorities = new ArrayList(); 40 | grantedAuthorities.add(new SimpleGrantedAuthority("ADMIN")); 41 | return new org.springframework.security.core.userdetails.User(lowercaseLogin, 42 | user.getPassword(), 43 | grantedAuthorities); 44 | 45 | }).orElseThrow(() -> 46 | new UsernameNotFoundException("用户名不存在")); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/security/UserNotActivatedException.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.security; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | /** 6 | * This exception is throw in case of a not activated user trying to authenticate. 7 | */ 8 | public class UserNotActivatedException extends AuthenticationException { 9 | 10 | public UserNotActivatedException(String message) { 11 | super(message); 12 | } 13 | 14 | public UserNotActivatedException(String message, Throwable t) { 15 | super(message, t); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/user/IUserService.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.user; 2 | 3 | import org.springframework.data.domain.Page; 4 | 5 | import com.lccf.base.service.IBaseService; 6 | import com.lccf.domain.User; 7 | 8 | /** 9 | * @author lichangchao 10 | * @Time 2017 -03-29 09:21:10 11 | */ 12 | public interface IUserService extends IBaseService { 13 | /** 14 | * 注册用户 15 | * 16 | * @param userParam 17 | * @return 18 | */ 19 | User register(UserParam userParam); 20 | 21 | /** 22 | * 获取用户数据 23 | * @param userParam 24 | * @return 25 | */ 26 | Page page(UserParam userParam); 27 | 28 | User getByUserName(String userName); 29 | } 30 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/user/UserParam.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.user; 2 | 3 | import com.lccf.base.service.PageParam; 4 | import javax.validation.constraints.NotNull; 5 | import javax.validation.constraints.Pattern; 6 | import lombok.Data; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @author lichangchao 13 | * @Time 2017 -04-13 20:22:31 14 | */ 15 | @Data 16 | public class UserParam extends PageParam implements Serializable { 17 | @NotNull(message = "用户名不能为空") 18 | @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,10}$",message = "用户名必须是数字和字母组合 6-10位之间") 19 | private String userName; 20 | 21 | private String realName; 22 | 23 | private String email; 24 | 25 | private String mobile; 26 | 27 | private String password; 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/user/UserVo.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.user; 2 | 3 | 4 | import lombok.Data; 5 | 6 | /** 7 | * @author lichangchao 8 | * @date 2017 -04-25 18:49:44 9 | */ 10 | @Data 11 | public class UserVo { 12 | private String userName; 13 | 14 | private String realName; 15 | 16 | private String email; 17 | 18 | private String mobile; 19 | 20 | private String password; 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lcc-web/src/main/java/com/lccf/service/user/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lccf.service.user.impl; 2 | 3 | 4 | import com.lccf.base.service.impl.BaseServiceImpl; 5 | import java.util.Date; 6 | import java.util.List; 7 | 8 | import javax.annotation.Resource; 9 | import javax.inject.Inject; 10 | import javax.transaction.Transactional; 11 | 12 | import org.springframework.data.domain.Example; 13 | import org.springframework.data.domain.Page; 14 | import org.springframework.data.domain.Pageable; 15 | import org.springframework.security.crypto.password.PasswordEncoder; 16 | import org.springframework.stereotype.Service; 17 | import org.springframework.util.CollectionUtils; 18 | 19 | import com.lccf.domain.User; 20 | import com.lccf.enums.EUserStatus; 21 | import com.lccf.repository.UserRepository; 22 | import com.lccf.service.user.IUserService; 23 | import com.lccf.service.user.UserParam; 24 | import com.lccf.service.user.UserVo; 25 | import com.lccf.util.BeanMapper; 26 | 27 | /** 28 | * @author lichangchao 29 | * @Time 2017 -03-29 09:21:05 30 | */ 31 | @Service 32 | @Transactional 33 | public class UserServiceImpl extends BaseServiceImpl implements IUserService { 34 | @Resource 35 | UserRepository userRepository; 36 | @Inject 37 | private PasswordEncoder passwordEncoder; 38 | 39 | @Override 40 | public User register(UserParam userParam) { 41 | User user = BeanMapper.map(userParam, User.class); 42 | user.setStatus(EUserStatus.FREEZE.getKey()); 43 | user.setPassword(passwordEncoder.encode(user.getPassword())); 44 | user.setCreateTime(new Date()); 45 | userRepository.save(user); 46 | return user; 47 | } 48 | 49 | @Override 50 | public Page page(UserParam userParam) { 51 | Pageable pageable = userParam.transPageRequest(); 52 | User user = BeanMapper.map(userParam, User.class); 53 | return userRepository.findAll(Example.of(user),pageable); 54 | } 55 | 56 | @Override 57 | public User getByUserName(String userName) { 58 | List list = userRepository.findByUserName(userName); 59 | if(CollectionUtils.isEmpty(list)){ 60 | return null; 61 | } 62 | return list.get(0); 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /lcc-web/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8070 3 | # 开发环境配置 4 | spring: 5 | profiles: 6 | active: dev 7 | devtools: 8 | restart: 9 | enabled: true 10 | livereload: 11 | enabled: false 12 | datasource: 13 | url: jdbc:mysql://47.94.196.111:3306/lccf?useUnicode=true&characterEncoding=utf8&useSSL=false 14 | name: 15 | username: root 16 | password: 111111 17 | hikari: 18 | data-source-properties: 19 | cachePrepStmts: true 20 | prepStmtCacheSize: 250 21 | prepStmtCacheSqlLimit: 2048 22 | useServerPrepStmts: true 23 | jpa: 24 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 25 | database: MYSQL 26 | show_sql: true 27 | properties: 28 | hibernate.cache.use_second_level_cache: true 29 | hibernate.hbm2ddl.auto: update 30 | #hibernate.show_sql:true #打印sql 31 | # spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下: 32 | # create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 33 | # create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。 34 | # update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。 35 | # validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 36 | cache: 37 | #缓存类型 38 | type: redis 39 | #缓存名称 40 | cache-names: redisCache 41 | #缓存最大数量500条, 缓存失效时间 6个小时 42 | guava.spec: maximumSize=500,expireAfterWrite=360m 43 | 44 | 45 | redis: 46 | database: 0 47 | cluster: 48 | nodes: 192.168.0.1 49 | password: 50 | timeout: 0 51 | pool: 52 | max-active: 8 53 | max-idle: 8 54 | max-wait: 1 55 | min-idle: 0 56 | mail: 57 | host: smtp.163.com 58 | username: licclife 59 | password: 1111111 60 | properties: 61 | mail: 62 | smtp: 63 | auth: true 64 | # =================================================================== 65 | # lccf specific properties 66 | # =================================================================== 67 | lccf: 68 | swagger: 69 | title: 后台管理系统 70 | description: boot+ng2 搭建后台管理系统 71 | version: 0.0.1 72 | termsOfServiceUrl: 73 | contact: 74 | license: 75 | licenseUrl: 76 | mail: 77 | from: licclife@163.com 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /lcc-web/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | 4 | # 开发环境配置 5 | spring: 6 | profiles: 7 | active: dev 8 | devtools: 9 | restart: 10 | enabled: true 11 | livereload: 12 | enabled: false 13 | datasource: 14 | url: jdbc:mysql://localhost:3306/lccf?useUnicode=true&characterEncoding=utf8&useSSL=false 15 | name: 16 | username: root 17 | password: 111111 18 | hikari: 19 | data-source-properties: 20 | cachePrepStmts: true 21 | prepStmtCacheSize: 250 22 | prepStmtCacheSqlLimit: 2048 23 | useServerPrepStmts: true 24 | jpa: 25 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 26 | database: MYSQL 27 | show_sql: true 28 | properties: 29 | hibernate.cache.use_second_level_cache: true 30 | hibernate.hbm2ddl.auto: create-drop 31 | hibernate.show_sql: true #打印sql 32 | # spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下: 33 | # create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 34 | # create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。 35 | # update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。 36 | # validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 37 | 38 | # =================================================================== 39 | # lccf specific properties 40 | # =================================================================== 41 | lccf: 42 | swagger: 43 | title: 后台管理系统 44 | description: boot+ng2 搭建后台管理系统 45 | version: 0.0.1 46 | termsOfServiceUrl: 47 | contact: 48 | license: 49 | licenseUrl: 50 | 51 | spring: 52 | cache: 53 | #缓存类型 54 | type: GUAVA 55 | #缓存名称 56 | cache-names: guavaCache 57 | #缓存最大数量500条, 缓存失效时间 6个小时 58 | guava.spec: maximumSize=500,expireAfterWrite=360m 59 | # REDIS (RedisProperties) 60 | # redis : 61 | # host : localhost # server host 62 | # port : 6379 # connection port 63 | # pool.max-idle : 8 # pool settings ... 64 | # pool.min-idle : 1 65 | # pool.max-active : 8 66 | # pool.max-wait : -1 -------------------------------------------------------------------------------- /lcc-web/src/main/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | 4 | # 开发环境配置 5 | spring: 6 | profiles: 7 | active: dev 8 | devtools: 9 | restart: 10 | enabled: true 11 | livereload: 12 | enabled: false 13 | datasource: 14 | url: jdbc:mysql://localhost:3306/lccf?useUnicode=true&characterEncoding=utf8&useSSL=false 15 | name: 16 | username: root 17 | password: 111111 18 | hikari: 19 | data-source-properties: 20 | cachePrepStmts: true 21 | prepStmtCacheSize: 250 22 | prepStmtCacheSqlLimit: 2048 23 | useServerPrepStmts: true 24 | jpa: 25 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 26 | database: MYSQL 27 | show_sql: true 28 | properties: 29 | hibernate.cache.use_second_level_cache: true 30 | hibernate.hbm2ddl.auto: create-drop 31 | hibernate.show_sql: true #打印sql 32 | # spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下: 33 | # create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 34 | # create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。 35 | # update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。 36 | # validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 37 | 38 | # =================================================================== 39 | # lccf specific properties 40 | # =================================================================== 41 | lccf: 42 | swagger: 43 | title: 后台管理系统 44 | description: boot+ng2 搭建后台管理系统 45 | version: 0.0.1 46 | termsOfServiceUrl: 47 | contact: 48 | license: 49 | licenseUrl: 50 | 51 | spring: 52 | cache: 53 | #缓存类型 54 | type: GUAVA 55 | #缓存名称 56 | cache-names: guavaCache 57 | #缓存最大数量500条, 缓存失效时间 6个小时 58 | guava.spec: maximumSize=500,expireAfterWrite=360m 59 | # REDIS (RedisProperties) 60 | # redis : 61 | # host : localhost # server host 62 | # port : 6379 # connection port 63 | # pool.max-idle : 8 # pool settings ... 64 | # pool.min-idle : 1 65 | # pool.max-active : 8 66 | # pool.max-wait : -1 -------------------------------------------------------------------------------- /lcc-web/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=dev 2 | logging.config=classpath:log4j2.yaml -------------------------------------------------------------------------------- /lcc-web/src/main/resources/i18n/messages_en.properties: -------------------------------------------------------------------------------- 1 | # Activation e-mail 2 | email.activation.title=tuxAdmin account activation 3 | email.activation.greeting=Dear {0} 4 | email.activation.text1=Your tuxAdmin account has been created, please click on the URL below to activate it: 5 | email.activation.text2=Regards, 6 | email.signature=tuxAdmin Team. 7 | 8 | # Reset e-mail 9 | email.reset.title=tuxAdmin password reset 10 | email.reset.greeting=Dear {0} 11 | email.reset.text1=For your tuxAdmin account a password reset was requested, please click on the URL below to reset it: 12 | email.reset.text2=Regards, -------------------------------------------------------------------------------- /lcc-web/src/main/resources/i18n/messages_zh_CN.properties: -------------------------------------------------------------------------------- 1 | # Activation e-mail 2 | email.activation.title=邮箱激活邮件 3 | email.activation.greeting=Dear {0} 4 | email.activation.text1=Your tuxAdmin account has been created, please click on the URL below to activate it: 5 | email.activation.text2=Regards, 6 | email.signature=tuxAdmin Team. 7 | 8 | # Reset e-mail 9 | email.reset.title=tuxAdmin password reset 10 | email.reset.greeting=Dear {0} 11 | email.reset.text1=For your tuxAdmin account a password reset was requested, please click on the URL below to reset it: 12 | email.reset.text2=Regards, -------------------------------------------------------------------------------- /lcc-web/src/main/resources/log4j2.yaml: -------------------------------------------------------------------------------- 1 | Configuration: 2 | status: warn 3 | 4 | Properties: # 定义全局变量 5 | Property: # 缺省配置(用于开发环境)。其他环境需要在VM参数中指定,如下: 6 | #测试:-Dlog.level.console=warn -Dlog.level.xjj=trace 7 | #生产:-Dlog.level.console=warn -Dlog.level.xjj=info 8 | - name: log.level.console 9 | value: trace 10 | - name: log.level.jumore 11 | value: debug 12 | - name: log.path 13 | value: c:\opt\logs 14 | - name: project.name 15 | value: admin 16 | 17 | Appenders: 18 | Console: #输出到控制台 19 | name: CONSOLE 20 | target: SYSTEM_OUT 21 | ThresholdFilter: 22 | level: ${sys:log.level.console} # “sys:”表示:如果VM参数中没指定这个变量值,则使用本文件中定义的缺省全局变量值 23 | onMatch: ACCEPT 24 | onMismatch: DENY 25 | PatternLayout: 26 | pattern: "%highlight{%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n}{FATAL=white,ERROR=red,WARN=blue, INFO=black, DEBUG=green, TRACE=blue}" 27 | RollingFile: # 输出到文件,超过128MB归档 28 | - name: ROLLING_FILE 29 | ignoreExceptions: false 30 | fileName: ${log.path}/${project.name}.log 31 | filePattern: "${log.path}/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log.gz" 32 | PatternLayout: 33 | pattern: '%highlight{%d{HH:mm:ss.SSS} [%t] %-5level %logger{1.}:%L - %msg%n}{%throwable{short.fileName}}{FATAL=white,ERROR=red, 34 | WARN=blue, INFO=black, DEBUG=green, TRACE=blue}' 35 | Policies: 36 | SizeBasedTriggeringPolicy: 37 | size: "128 MB" 38 | DefaultRolloverStrategy: 39 | max: 1000 40 | 41 | Loggers: 42 | Root: 43 | level: info 44 | AppenderRef: 45 | - ref: CONSOLE 46 | - ref: ROLLING_FILE 47 | Logger: # 为com.xjj包配置特殊的Log级别,方便调试 48 | - name: com.jumore 49 | additivity: false 50 | level: ${sys:log.level.jumore} 51 | AppenderRef: 52 | - ref: CONSOLE 53 | - ref: ROLLING_FILE -------------------------------------------------------------------------------- /lcc-web/src/main/resources/mails/activationEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster activation 5 | 6 | 7 | 8 |

9 | Dear 10 |

11 |

12 | Your JHipster account has been created, please click on the URL below to activate it: 13 |

14 |

15 | 16 | Activation Link

17 |

18 | Regards, 19 |
20 | JHipster. 21 |

22 | 23 | 24 | -------------------------------------------------------------------------------- /lcc-web/src/main/resources/mails/creationEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster creation 5 | 6 | 7 | 8 |

9 | Dear 10 |

11 |

12 | Your JHipster account has been created, please click on the URL below to activate it: 13 |

14 |

15 | userName 17 |

18 |

19 | Regards, 20 |
21 | JHipster. 22 |

23 | 24 | 25 | -------------------------------------------------------------------------------- /lcc-web/src/main/resources/mails/passwordResetEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JHipster password reset 5 | 6 | 7 | 8 |

9 | Dear 10 |

11 |

12 | For your JHipster account a password reset was requested, please click on the URL below to reset it: 13 |

14 |

15 | Reset Link 17 |

18 |

19 | Regards, 20 |
21 | JHipster. 22 |

23 | 24 | 25 | -------------------------------------------------------------------------------- /lcc-web/src/main/resources/mails/tradeEmail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 中国比特币价格提醒 5 | 6 | 7 | 8 | 当前比特币价格:

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lcc-web/src/test/java/com/lccf/Documentation.java: -------------------------------------------------------------------------------- 1 | package com.lccf; 2 | 3 | import static org.springframework.http.MediaType.APPLICATION_JSON; 4 | import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; 5 | import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; 6 | import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; 7 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 8 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 9 | 10 | import org.junit.After; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; 15 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | import org.springframework.http.MediaType; 18 | import org.springframework.test.context.junit4.SpringRunner; 19 | import org.springframework.test.web.servlet.MockMvc; 20 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers; 21 | 22 | import io.github.robwin.markup.builder.MarkupLanguage; 23 | import io.github.robwin.swagger2markup.GroupBy; 24 | import io.github.robwin.swagger2markup.Swagger2MarkupConverter; 25 | import springfox.documentation.staticdocs.SwaggerResultHandler; 26 | 27 | @AutoConfigureMockMvc 28 | @AutoConfigureRestDocs(outputDir = "target/generated-snippets") 29 | @RunWith(SpringRunner.class) 30 | @SpringBootTest 31 | public class Documentation { 32 | 33 | private String snippetDir = "target/generated-snippets"; 34 | private String outputDir = "target/asciidoc"; 35 | // private String indexDoc = "docs/asciidoc/index.adoc"; 36 | 37 | @Autowired 38 | private MockMvc mockMvc; 39 | 40 | @After 41 | public void Test() throws Exception { 42 | // 得到swagger.json,写入outputDir目录中 43 | mockMvc.perform(get("/v2/api-docs").accept(APPLICATION_JSON)).andDo(SwaggerResultHandler.outputDirectory(outputDir).build()) 44 | .andExpect(MockMvcResultMatchers.status().isOk()).andReturn(); 45 | 46 | // 读取上一步生成的swagger.json转成asciiDoc,写入到outputDir 47 | // 这个outputDir必须和插件里面标签配置一致 48 | Swagger2MarkupConverter.from(outputDir + "/swagger.json").withPathsGroupedBy(GroupBy.TAGS)// 按tag排序 49 | .withMarkupLanguage(MarkupLanguage.ASCIIDOC)// 格式 50 | .withExamples(snippetDir).build().intoFolder(outputDir);// 输出 51 | 52 | } 53 | 54 | @Test 55 | public void testLogin() throws Exception { 56 | mockMvc.perform(get("/login").param("userName", "admin1").param("password", "111111").accept(MediaType.APPLICATION_JSON)) 57 | .andExpect(status().isOk()).andDo(document("login", preprocessResponse(prettyPrint()))); 58 | 59 | } 60 | 61 | @Test 62 | public void TestApi() throws Exception { 63 | 64 | mockMvc.perform(get("/api/menu/list").cookie().accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) 65 | .andDo(document("list", preprocessResponse(prettyPrint()))); 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /lcc-web/src/test/java/com/lccf/Swagger2MarkupResultHandlerUtf8.java: -------------------------------------------------------------------------------- 1 | package com.lccf; 2 | 3 | import io.github.robwin.markup.builder.MarkupLanguage; 4 | import io.github.robwin.swagger2markup.Swagger2MarkupConverter; 5 | import org.apache.commons.lang3.Validate; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.mock.web.MockHttpServletResponse; 9 | import org.springframework.test.web.servlet.MvcResult; 10 | import org.springframework.test.web.servlet.ResultHandler; 11 | 12 | 13 | public class Swagger2MarkupResultHandlerUtf8 implements ResultHandler { 14 | private static final Logger LOG = LoggerFactory.getLogger(Swagger2MarkupResultHandlerUtf8.class); 15 | 16 | private final String outputDir; 17 | private final MarkupLanguage markupLanguage; 18 | private final String examplesFolderPath; 19 | private final String encoding = "UTF-8"; 20 | 21 | Swagger2MarkupResultHandlerUtf8(String outputDir, MarkupLanguage markupLanguage, String examplesFolderPath) { 22 | this.outputDir = outputDir; 23 | this.markupLanguage = markupLanguage; 24 | this.examplesFolderPath = examplesFolderPath; 25 | } 26 | 27 | /** 28 | * Creates a Swagger2MarkupResultHandler.Builder 29 | * 30 | * @param outputDir the target folder 31 | * @return a Swagger2MarkupResultHandler.Builder 32 | */ 33 | public static Builder outputDirectory(String outputDir) { 34 | Validate.notEmpty(outputDir, "outputDir must not be empty!"); 35 | return new Builder(outputDir); 36 | } 37 | 38 | /** 39 | * Apply the action on the given result. 40 | * 41 | * @param result the result of the executed request 42 | * @throws Exception if a failure occurs 43 | */ 44 | @Override 45 | public void handle(MvcResult result) throws Exception { 46 | MockHttpServletResponse response = result.getResponse(); 47 | response.setCharacterEncoding(encoding); 48 | String swaggerJson = response.getContentAsString(); 49 | Swagger2MarkupConverter.fromString(swaggerJson).withMarkupLanguage(markupLanguage) 50 | .withExamples(examplesFolderPath).build().intoFolder(outputDir); 51 | } 52 | 53 | 54 | public static class Builder { 55 | private final String outputDir; 56 | private String examplesFolderPath; 57 | private MarkupLanguage markupLanguage = MarkupLanguage.ASCIIDOC; 58 | 59 | Builder(String outputDir) { 60 | this.outputDir = outputDir; 61 | } 62 | 63 | /** 64 | * Builds Swagger2MarkupResultHandler which converts the Swagger response into Markup and writes into the given {@code 65 | * outputDir}. 66 | * 67 | * @return a Mock MVC {@code ResultHandler} that will produce the documentation 68 | * @see org.springframework.test.web.servlet.MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder) 69 | * @see org.springframework.test.web.servlet.ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler) 70 | */ 71 | public Swagger2MarkupResultHandlerUtf8 build() { 72 | 73 | return new Swagger2MarkupResultHandlerUtf8(outputDir, markupLanguage, 74 | examplesFolderPath); 75 | } 76 | 77 | /** 78 | * Specifies the markup language which should be used to generate the files 79 | * 80 | * @param markupLanguage the markup language which is used to generate the files 81 | * @return the Swagger2MarkupConverter.Builder 82 | */ 83 | public Builder withMarkupLanguage(MarkupLanguage markupLanguage) { 84 | this.markupLanguage = markupLanguage; 85 | return this; 86 | } 87 | 88 | /** 89 | * Include examples into the Paths document 90 | * 91 | * @param examplesFolderPath the path to the folder where the example documents reside 92 | * @return the Swagger2MarkupConverter.Builder 93 | */ 94 | public Builder withExamples(String examplesFolderPath) { 95 | this.examplesFolderPath = examplesFolderPath; 96 | return this; 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /lccf-base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | lccf 7 | com.lccf 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.lccf.base 13 | base 14 | 15 | 16 | 17 | com.lccf.util 18 | lccf-util 19 | ${project.version} 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lccf-base/src/main/java/com/lccf/base/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | package com.lccf.base.controller; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import javax.annotation.Resource; 6 | 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | 10 | 11 | @RequestMapping("api") 12 | public class BaseController { 13 | @Resource 14 | RedisTemplate redisTemplate; 15 | public final static String EMAIL_KEY= "email_key."; 16 | /** 17 | * 频率限制 18 | * @param key 19 | * @param time 时间 20 | * @param count 限制次数 21 | * @return 22 | */ 23 | public Boolean apiFrequency(String key,Long time ,Integer count){ 24 | Long value = redisTemplate.opsForValue().increment(key, 1L); 25 | redisTemplate.expire(key, time, TimeUnit.SECONDS); 26 | if(value > count) { 27 | return false; 28 | } 29 | return true; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lccf-base/src/main/java/com/lccf/base/controller/DownloadController.java: -------------------------------------------------------------------------------- 1 | package com.lccf.base.controller; 2 | 3 | import io.swagger.annotations.ApiImplicitParam; 4 | import io.swagger.annotations.ApiImplicitParams; 5 | import io.swagger.annotations.ApiOperation; 6 | import java.io.IOException; 7 | import org.springframework.core.io.ClassPathResource; 8 | import org.springframework.core.io.InputStreamResource; 9 | import org.springframework.http.HttpHeaders; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | /** 17 | * Function:(这里用一句话描述这个类的作用) 18 | * 19 | * @author Administrator 20 | * @version 1.0.0 21 | * @date 2017/10/19 18:24 22 | * @see 23 | */ 24 | @RestController 25 | public class DownloadController extends BaseController{ 26 | @RequestMapping(value = "/download", method = RequestMethod.GET) 27 | @ApiOperation(value = "文件下載", httpMethod = "GET", response = byte.class) 28 | @ApiImplicitParams({ 29 | @ApiImplicitParam(name = "fileName", value = "文件名称", paramType = "query"), 30 | @ApiImplicitParam(name = "fileType", value = "文件类型", paramType = "query") 31 | }) 32 | public ResponseEntity downloadFile(String fileName, String fileType) 33 | throws IOException { 34 | String filePath = fileName+"."+fileType; 35 | ClassPathResource file = new ClassPathResource("down/"+filePath); 36 | 37 | HttpHeaders headers = new HttpHeaders(); 38 | headers.add("Cache-Control", "no-cache, no-store, must-revalidate"); 39 | headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getFilename())); 40 | headers.add("Pragma", "no-cache"); 41 | headers.add("Expires", "0"); 42 | return ResponseEntity 43 | .ok() 44 | .headers(headers) 45 | .contentLength(file.contentLength()) 46 | .contentType(MediaType.parseMediaType("application/octet-stream")) 47 | .body(new InputStreamResource(file.getInputStream())); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lccf-base/src/main/java/com/lccf/base/param/BaseParam.java: -------------------------------------------------------------------------------- 1 | package com.lccf.base.param; 2 | 3 | import java.util.Date; 4 | import lombok.Data; 5 | 6 | /** 7 | * 8 | * 9 | * @author lichangchao 10 | * @version 1.0.0 11 | * @date 2017/10/11 18:14 12 | * @see 13 | */ 14 | @Data 15 | public class BaseParam { 16 | private Long id; 17 | private Date createTime; 18 | private Date updateTime; 19 | 20 | public void setCreateTime(Date createTime) { 21 | if (this.id == null) { 22 | this.createTime = new Date(); 23 | } 24 | 25 | } 26 | 27 | public void setUpdateTime(Date updateTime) { 28 | if (this.id != null) { 29 | this.updateTime = new Date(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lccf-base/src/main/java/com/lccf/base/param/PageParam.java: -------------------------------------------------------------------------------- 1 | package com.lccf.base.service; 2 | 3 | import com.lccf.base.param.BaseParam; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Sort; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * 分页参数 11 | * 12 | * @author lichangchao 13 | * @date 2017 -04-25 19:25:03 14 | */ 15 | @Data 16 | public class PageParam extends BaseParam { 17 | private Integer page = 0; 18 | private Integer size = 10; 19 | private Sort sort; 20 | 21 | public PageRequest transPageRequest() { 22 | return new PageRequest(this.page, this.size, this.sort); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lccf-base/src/main/java/com/lccf/base/service/IBaseService.java: -------------------------------------------------------------------------------- 1 | package com.lccf.base.service; 2 | 3 | /** 4 | * @author lichangchao 5 | * @date 2017 -05-02 21:12:47 6 | */ 7 | public interface IBaseService { 8 | /** 9 | * 物理删除 10 | * @param id 11 | */ 12 | void deleteById(Long id); 13 | 14 | /** 15 | * 新增或者修改 16 | * @param param 17 | */ 18 | void save(P param); 19 | 20 | /** 21 | * 逻辑删除 22 | * @param id 23 | */ 24 | void updateDeleteFlagById(Long id) ; 25 | 26 | 27 | T findOne(Long id); 28 | } 29 | -------------------------------------------------------------------------------- /lccf-base/src/main/java/com/lccf/base/service/impl/BaseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lccf.base.service.impl; 2 | 3 | import com.lccf.base.service.IBaseService; 4 | import com.lccf.util.BeanMapper; 5 | import com.lccf.util.GenericsUtils; 6 | import com.lccf.util.Reflect; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.core.convert.converter.Converter; 9 | import org.springframework.data.domain.Page; 10 | import org.springframework.data.jpa.repository.JpaRepository; 11 | 12 | /** 13 | * 14 | *
    15 | *

    参数说明:

    16 | *
  • T:对应数据库持久化bean
  • 17 | *
  • P:页面接受参数
  • 18 | *
  • V:返回参数
  • 19 | *
20 | * @author lichangchao 21 | * @date 2017 -05-02 21:08:56 22 | */ 23 | 24 | public class BaseServiceImpl implements IBaseService { 25 | private transient Class DTOClass = GenericsUtils.getSuperClassGenricType(this.getClass(), 0); 26 | private transient Class VOClass = GenericsUtils.getSuperClassGenricType(this.getClass(), 2); 27 | 28 | @Autowired 29 | JpaRepository jpaRepository; 30 | 31 | @Override 32 | public void deleteById(Long id) { 33 | jpaRepository.delete(id); 34 | } 35 | 36 | 37 | @Override 38 | public void save(P param) { 39 | if (param == null) { 40 | throw new IllegalArgumentException("参数为空"); 41 | } 42 | T t = BeanMapper.map(param, DTOClass); 43 | jpaRepository.save(t); 44 | } 45 | 46 | @Override 47 | public void updateDeleteFlagById(Long id) { 48 | T t = jpaRepository.findOne(id); 49 | Reflect.on(t).set("deleteFlag", true); 50 | jpaRepository.save(t); 51 | } 52 | 53 | @Override 54 | public T findOne(Long id) { 55 | return jpaRepository.findOne(id); 56 | } 57 | 58 | public Page pageDtoToPageVo(Page tPage) { 59 | if (tPage == null) { 60 | return null; 61 | } 62 | return tPage.map(new Converter() { 63 | @Override 64 | public V convert(T t) { 65 | return BeanMapper.map(t, VOClass); 66 | } 67 | }); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /lccf-util/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | lccf 7 | com.lccf 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.lccf.util 13 | lccf-util 14 | 15 | 3.13 16 | 17 | 18 | 19 | 20 | org.apache.poi 21 | poi 22 | ${poi.version} 23 | 24 | 25 | org.apache.poi 26 | poi-ooxml 27 | ${poi.version} 28 | 29 | 30 | org.apache.poi 31 | poi-ooxml-schemas 32 | ${poi.version} 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/constants/Constants.java: -------------------------------------------------------------------------------- 1 | package com.lccf.constants; 2 | 3 | 4 | public final class Constants { 5 | public static final String SPRING_PROFILE_DEVELOPMENT = "dev"; 6 | public static final String SPRING_PROFILE_PRODUCTION = "prod"; 7 | public static final String SPRING_PROFILE_FAST = "fast"; 8 | public static final String SPRING_PROFILE_CLOUD = "cloud"; 9 | public static final String SPRING_PROFILE_HEROKU = "heroku"; 10 | public static final String SYSTEM_ACCOUNT = "system"; 11 | 12 | private Constants() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/constants/LccfProperties.java: -------------------------------------------------------------------------------- 1 | package com.lccf.constants; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | 6 | @ConfigurationProperties(prefix = "lccf", ignoreUnknownFields = false) 7 | public class LccfProperties { 8 | private final Swagger swagger = new Swagger(); 9 | private final Mail mail = new Mail(); 10 | public Swagger getSwagger() { 11 | return swagger; 12 | } 13 | public Mail getMail() { 14 | return mail; 15 | } 16 | 17 | public static class Swagger { 18 | 19 | private String title = "lccfApi"; 20 | 21 | private String description = "lccf API documentation"; 22 | 23 | private String version = "0.0.1"; 24 | 25 | private String termsOfServiceUrl; 26 | 27 | private String contact; 28 | 29 | private String license; 30 | 31 | private String licenseUrl; 32 | 33 | public String getTitle() { 34 | return title; 35 | } 36 | 37 | public void setTitle(String title) { 38 | this.title = title; 39 | } 40 | 41 | public String getDescription() { 42 | return description; 43 | } 44 | 45 | public void setDescription(String description) { 46 | this.description = description; 47 | } 48 | 49 | public String getVersion() { 50 | return version; 51 | } 52 | 53 | public void setVersion(String version) { 54 | this.version = version; 55 | } 56 | 57 | public String getTermsOfServiceUrl() { 58 | return termsOfServiceUrl; 59 | } 60 | 61 | public void setTermsOfServiceUrl(String termsOfServiceUrl) { 62 | this.termsOfServiceUrl = termsOfServiceUrl; 63 | } 64 | 65 | public String getContact() { 66 | return contact; 67 | } 68 | 69 | public void setContact(String contact) { 70 | this.contact = contact; 71 | } 72 | 73 | public String getLicense() { 74 | return license; 75 | } 76 | 77 | public void setLicense(String license) { 78 | this.license = license; 79 | } 80 | 81 | public String getLicenseUrl() { 82 | return licenseUrl; 83 | } 84 | 85 | public void setLicenseUrl(String licenseUrl) { 86 | this.licenseUrl = licenseUrl; 87 | } 88 | } 89 | public static class Mail { 90 | 91 | private String from = "tuxAdmin@localhost"; 92 | 93 | public String getFrom() { 94 | return from; 95 | } 96 | 97 | public void setFrom(String from) { 98 | this.from = from; 99 | } 100 | } 101 | 102 | } 103 | 104 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/enums/ECustomUUID.java: -------------------------------------------------------------------------------- 1 | package com.lccf.enums; 2 | 3 | import com.lccf.util.CustomUUID; 4 | 5 | /** 6 | * 生成全局唯一码 采用单列模式 7 | * @see 简书介绍 8 | * 9 | * @author lichangchao 10 | * @Time 2017 -04-13 20:44:26 11 | */ 12 | public enum ECustomUUID { 13 | INSTANCE; 14 | private CustomUUID customUUID; 15 | ECustomUUID(){ 16 | customUUID = new CustomUUID(12); 17 | } 18 | public CustomUUID getInstance() { 19 | return customUUID; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/enums/EResultCode.java: -------------------------------------------------------------------------------- 1 | package com.lccf.enums; 2 | 3 | /** 4 | * @author lichangchao 5 | * @功能 统一返回码 6 | * 7 | */ 8 | public enum EResultCode { 9 | SUCCESS(200,"操作成功"), 10 | FAIL(0,"操作失败"); 11 | private int key; 12 | private String value; 13 | EResultCode(int key, String value){ 14 | this.key = key; 15 | this.value = value; 16 | } 17 | public int getKey() { 18 | return key; 19 | } 20 | public String getValue() { 21 | return value; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/enums/ETrade.java: -------------------------------------------------------------------------------- 1 | package com.lccf.enums; 2 | 3 | /** 4 | * 5 | * 6 | * @author lichangchao 7 | * @date 2017 /5/17 11:09 8 | * @version 1.0.0 9 | * @see 10 | */ 11 | public enum ETrade { 12 | ETC("etc"), 13 | LTC("ltc"); 14 | private String value; 15 | ETrade( String value){ 16 | this.value = value; 17 | } 18 | 19 | public String getValue() { 20 | return value; 21 | } 22 | 23 | public void setValue(String value) { 24 | this.value = value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/enums/EUserStatus.java: -------------------------------------------------------------------------------- 1 | package com.lccf.enums; 2 | 3 | 4 | /** 5 | * @author lichangchao 6 | * @Time 2017 -04-06 16:33:06 7 | */ 8 | public enum EUserStatus { 9 | FREEZE(0,"激活状态"), 10 | ACTIVITED(1,"未激活状态"); 11 | private int key; 12 | private String value; 13 | EUserStatus(int key, String value){ 14 | this.key = key; 15 | this.value = value; 16 | } 17 | 18 | public int getKey() { 19 | return key; 20 | } 21 | 22 | 23 | 24 | public String getValue() { 25 | return value; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/enums/StatusEmun.java: -------------------------------------------------------------------------------- 1 | package com.lccf.enums; 2 | 3 | /** 4 | * 邮件发送状态 5 | * @author lichangchao 6 | * @version 1.0.0 7 | * @date 2017/12/4 18:49 8 | * @see 9 | */ 10 | public enum StatusEmun { 11 | // 利用构造函数传参 12 | FAIL (0), SUCCESS (1); 13 | 14 | // 定义私有变量 15 | private int code ; 16 | 17 | // 构造函数,枚举类型只能为私有 18 | private StatusEmun( int code) { 19 | this . code = code; 20 | } 21 | 22 | public int getCode() { 23 | return code; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return String.valueOf ( this . code ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/BeanMapper.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import com.google.common.collect.Lists; 4 | import org.dozer.DozerBeanMapper; 5 | 6 | import java.util.Collection; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | 10 | public class BeanMapper 11 | { 12 | private static DozerBeanMapper dozer = new DozerBeanMapper(); 13 | 14 | /** 15 | * 构造新的destinationClass实例对象,通过source对象中的字段内容 16 | * 映射到destinationClass实例对象中,并返回新的destinationClass实例对象。 17 | * 18 | * @param source 源数据对象 19 | * @param destinationClass 要构造新的实例对象Class 20 | */ 21 | public static T map(Object source, Class destinationClass) 22 | { 23 | return dozer.map(source, destinationClass); 24 | } 25 | 26 | 27 | 28 | public static Collection mapList(Collection sourceList, Class destinationClass) 29 | { 30 | List destinationList = Lists.newArrayList(); 31 | for (Iterator i$ = sourceList.iterator(); i$.hasNext(); ) { Object sourceObject = i$.next(); 32 | Object destinationObject = dozer.map(sourceObject, destinationClass); 33 | destinationList.add(destinationObject); 34 | } 35 | return destinationList; 36 | } 37 | 38 | 39 | 40 | 41 | /** 42 | * 将对象source的所有属性值拷贝到对象destination中. 43 | * 44 | * @param source 对象source 45 | * @param destinationObject 对象destination 46 | */ 47 | public static void copy(Object source, Object destinationObject) 48 | { 49 | dozer.map(source, destinationObject); 50 | } 51 | } -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/Collections3.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.base.Joiner; 5 | import com.google.common.base.Splitter; 6 | import com.google.common.collect.Lists; 7 | import org.apache.commons.beanutils.BeanUtils; 8 | import org.springframework.util.CollectionUtils; 9 | import org.springframework.util.StringUtils; 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | /** 15 | * 集合工具类 16 | * 17 | * @author lichangchao 18 | */ 19 | public class Collections3 { 20 | /** 21 | * LIST 对象转化成String 22 | * 23 | * @param list 目标对象 24 | * @param clumName 对象里面的字段名称 25 | * @param separator 分隔符 26 | * @return 27 | */ 28 | public static String transListToString(List list, final String clumName, String separator) { 29 | if (CollectionUtils.isEmpty(list)) return ""; 30 | Function trans = new Function() { 31 | @Override 32 | public String apply(Object obj) { 33 | String s = ""; 34 | try { 35 | s = BeanUtils.getProperty(obj, clumName); 36 | } catch (Exception e) { 37 | e.printStackTrace(); 38 | return ""; 39 | } 40 | return s; 41 | } 42 | }; 43 | return Joiner.on(separator).join(Lists.transform(list, trans)); 44 | } 45 | 46 | /** 47 | * String 转 List 48 | * 49 | * @param target 50 | * @param separator 51 | * @param 52 | * @return 53 | */ 54 | public static List transStringToList(String target, String separator) { 55 | if (StringUtils.isEmpty(target)) return Collections.EMPTY_LIST; 56 | List retList = new ArrayList<>(); 57 | List list = Splitter.on(separator).trimResults().splitToList(target); 58 | for (String s : list) { 59 | retList.add((T) s); 60 | } 61 | return CollectionUtils.isEmpty(retList) ? Collections.EMPTY_LIST : retList; 62 | 63 | } 64 | 65 | /** 66 | * String 转 Long 67 | * 68 | * @param target 69 | * @param separator 70 | * @return 71 | */ 72 | public static Long[] transStringToArray(String target, String separator) { 73 | if (StringUtils.isEmpty(target)) return null; 74 | String[] ss = target.split(separator); 75 | Long[] ls = new Long[ss.length]; 76 | for (int i = 0; i < ss.length; i++) { 77 | ls[i] = Long.valueOf(ss[i]); 78 | } 79 | return ls; 80 | } 81 | 82 | /** 83 | * @param targetList 目标list 84 | * @param start 截取开始的位置 85 | * @param end 截取结束的位置 86 | * @return List 87 | */ 88 | public static List subList(List targetList, int start, int end) { 89 | if (CollectionUtils.isEmpty(targetList)) return Collections.EMPTY_LIST; 90 | List list = new ArrayList(end - start); 91 | int i = 0; 92 | for (T t : targetList) { 93 | if (i >= start && i < end) { 94 | list.add(t); 95 | } 96 | i++; 97 | } 98 | return list; 99 | } 100 | 101 | /** 102 | * @param targetList 目标list 103 | * 顺序去掉重复 104 | * @return List 105 | */ 106 | public static List deleteRepeat(List... targetList) { 107 | if (targetList == null) return Collections.EMPTY_LIST; 108 | List list = new ArrayList(); 109 | for (int i = 0; i < targetList.length; i++) { 110 | List tList = targetList[i]; 111 | for (T t : tList) { 112 | if (!list.contains(t)) { 113 | list.add(t); 114 | } 115 | } 116 | } 117 | return list; 118 | } 119 | 120 | public static void main(String arge[]) { 121 | List list = new ArrayList<>(); 122 | for (int i = 0; i < 100; i++) { 123 | list.add(i); 124 | } 125 | list = (List) Collections3.subList(list, 2, 10); 126 | for (int j : list) { 127 | System.out.println(j); 128 | } 129 | 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/CookieUtil.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import javax.servlet.http.Cookie; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import org.springframework.web.util.WebUtils; 7 | public class CookieUtil { 8 | public static void create(HttpServletResponse httpServletResponse, String name, String value, Boolean secure, Integer maxAge, String domain) { 9 | Cookie cookie = new Cookie(name, value); 10 | cookie.setSecure(secure); 11 | cookie.setHttpOnly(false); 12 | cookie.setMaxAge(maxAge); 13 | // cookie.setDomain(domain); 14 | cookie.setPath("/"); 15 | httpServletResponse.addCookie(cookie); 16 | } 17 | 18 | public static void clear(HttpServletResponse httpServletResponse, String name) { 19 | Cookie cookie = new Cookie(name, null); 20 | cookie.setPath("/"); 21 | cookie.setHttpOnly(false); 22 | cookie.setMaxAge(0); 23 | httpServletResponse.addCookie(cookie); 24 | } 25 | 26 | public static String getValue(HttpServletRequest httpServletRequest, String name) { 27 | Cookie cookie = WebUtils.getCookie(httpServletRequest, name); 28 | return cookie != null ? cookie.getValue() : null; 29 | } 30 | } -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/CustomUUID.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import com.lccf.enums.ECustomUUID; 4 | 5 | import java.security.SecureRandom; 6 | 7 | /** 8 | * 自定义 ID 生成器 9 | * ID 生成规则: ID长达 64 bits 10 | * 11 | * | 41 bits: Timestamp (毫秒) | 3 bits: 区域(机房) | 10 bits: 机器编号 | 10 bits: 序列号 | 12 | */ 13 | public class CustomUUID { 14 | // 基准时间 15 | private long twepoch = 1288834974657L; //Thu, 04 Nov 2010 01:42:54 GMT 16 | // 区域标志位数 17 | private final static long regionIdBits = 3L; 18 | // 机器标识位数 19 | private final static long workerIdBits = 10L; 20 | // 序列号识位数 21 | private final static long sequenceBits = 10L; 22 | 23 | // 区域标志ID最大值 24 | private final static long maxRegionId = -1L ^ (-1L << regionIdBits); 25 | // 机器ID最大值 26 | private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); 27 | // 序列号ID最大值 28 | private final static long sequenceMask = -1L ^ (-1L << sequenceBits); 29 | 30 | // 机器ID偏左移10位 31 | private final static long workerIdShift = sequenceBits; 32 | // 业务ID偏左移20位 33 | private final static long regionIdShift = sequenceBits + workerIdBits; 34 | // 时间毫秒左移23位 35 | private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits; 36 | 37 | private static long lastTimestamp = -1L; 38 | 39 | private long sequence = 0L; 40 | private final long workerId; 41 | private final long regionId; 42 | 43 | public CustomUUID(long workerId, long regionId) { 44 | 45 | // 如果超出范围就抛出异常 46 | if (workerId > maxWorkerId || workerId < 0) { 47 | throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0"); 48 | } 49 | if (regionId > maxRegionId || regionId < 0) { 50 | throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0"); 51 | } 52 | 53 | this.workerId = workerId; 54 | this.regionId = regionId; 55 | } 56 | 57 | public CustomUUID(long workerId) { 58 | // 如果超出范围就抛出异常 59 | if (workerId > maxWorkerId || workerId < 0) { 60 | throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0"); 61 | } 62 | this.workerId = workerId; 63 | this.regionId = 0; 64 | } 65 | 66 | public long generate() { 67 | return this.nextId(false, 0); 68 | } 69 | 70 | /** 71 | * 实际产生代码的 72 | * 73 | * @param isPadding 74 | * @param busId 75 | * @return 76 | */ 77 | private synchronized long nextId(boolean isPadding, long busId) { 78 | 79 | long timestamp = timeGen(); 80 | long paddingnum = regionId; 81 | 82 | if (isPadding) { 83 | paddingnum = busId; 84 | } 85 | 86 | if (timestamp < lastTimestamp) { 87 | try { 88 | throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds"); 89 | } catch (Exception e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | 94 | //如果上次生成时间和当前时间相同,在同一毫秒内 95 | if (lastTimestamp == timestamp) { 96 | //sequence自增,因为sequence只有10bit,所以和sequenceMask相与一下,去掉高位 97 | sequence = (sequence + 1) & sequenceMask; 98 | //判断是否溢出,也就是每毫秒内超过1024,当为1024时,与sequenceMask相与,sequence就等于0 99 | if (sequence == 0) { 100 | //自旋等待到下一毫秒 101 | timestamp = tailNextMillis(lastTimestamp); 102 | } 103 | } else { 104 | // 如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加, 105 | // 为了保证尾数随机性更大一些,最后一位设置一个随机数 106 | sequence = new SecureRandom().nextInt(10); 107 | } 108 | 109 | lastTimestamp = timestamp; 110 | 111 | return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift) | (workerId << workerIdShift) | sequence; 112 | } 113 | 114 | // 防止产生的时间比之前的时间还要小(由于NTP回拨等问题),保持增量的趋势. 115 | private long tailNextMillis(final long lastTimestamp) { 116 | long timestamp = this.timeGen(); 117 | while (timestamp <= lastTimestamp) { 118 | timestamp = this.timeGen(); 119 | } 120 | return timestamp; 121 | } 122 | 123 | // 获取当前的时间戳 124 | protected long timeGen() { 125 | return System.currentTimeMillis(); 126 | } 127 | 128 | } -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/GenericsUtils.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import java.lang.reflect.ParameterizedType; 4 | import java.lang.reflect.Type; 5 | import java.lang.reflect.TypeVariable; 6 | import java.util.*; 7 | 8 | 9 | /** 10 | * 泛型工具类 11 | */ 12 | public class GenericsUtils { 13 | 14 | /** 15 | * 获得指定类类的泛型参数类型 16 | * @param clazz Class 17 | * @param index 泛型参数所在索引,从0开始 18 | * @return Class 19 | */ 20 | @SuppressWarnings("rawtypes") 21 | public static Class getSuperClassGenricType(Class clazz, int index) { 22 | if (clazz == null) { 23 | return null; 24 | } 25 | 26 | Type genericType = clazz.getGenericSuperclass(); 27 | while (genericType != null && !(genericType instanceof ParameterizedType)) { 28 | clazz = clazz.getSuperclass(); 29 | if (clazz == null) { 30 | break; 31 | } else { 32 | genericType = clazz.getGenericSuperclass(); 33 | } 34 | } 35 | 36 | if (!(genericType instanceof ParameterizedType)) { 37 | return Object.class; 38 | } 39 | 40 | Type[] params = ((ParameterizedType) genericType).getActualTypeArguments(); 41 | if (params != null && index >= 0 && index < params.length ) { 42 | if (params[index] instanceof Class) { 43 | return (Class) params[index]; 44 | } 45 | } 46 | 47 | return Object.class; 48 | } 49 | 50 | 51 | /** 52 | * 获取接口的泛型类型 53 | * @param clazz 目标类 54 | * @param interface1 指定接口 55 | * @param index 指定泛型index 56 | * @return 57 | */ 58 | public static Class getInterfaceGenricType(Class clazz, Class interface1, int index) { 59 | if (clazz == null || interface1 == null) { 60 | return null; 61 | } 62 | 63 | Type foundInteface = null; 64 | Type[] interfaces = clazz.getGenericInterfaces(); 65 | for (Type iType : interfaces) { 66 | if (!(iType instanceof ParameterizedType)) { 67 | continue; 68 | } 69 | 70 | if (((ParameterizedType) iType).getRawType() == interface1) { 71 | foundInteface = iType; 72 | } 73 | } 74 | 75 | if (foundInteface == null) { 76 | return null; 77 | } 78 | 79 | 80 | Type[] params = ((ParameterizedType) foundInteface).getActualTypeArguments(); 81 | if (params != null && index >= 0 && index < params.length ) { 82 | if (params[index] instanceof Class) { 83 | return (Class) params[index]; 84 | } 85 | } 86 | 87 | return Object.class; 88 | } 89 | 90 | 91 | /** 92 | * 获取实际类参数 93 | * @param actual 最终类 94 | * @param generic 泛型声明类 95 | * @return 96 | * @throws ClassNotFoundException 97 | */ 98 | @SuppressWarnings({ "rawtypes", "unused" }) 99 | private static Map getActualTypeParametersMap(Class actual, Class generic) { 100 | List names = new ArrayList(); 101 | for (TypeVariable t : generic.getTypeParameters()) { 102 | names.add(t.getName()); 103 | } 104 | 105 | List values = new ArrayList(); 106 | for (Type t : actual.getGenericInterfaces()) { 107 | if (t instanceof Class) 108 | continue; 109 | if (((ParameterizedType) t).getRawType().equals(generic)) { 110 | Collections.addAll(values, ((ParameterizedType) t).getActualTypeArguments()); 111 | break; 112 | } 113 | } 114 | if (values.size() == 0) { 115 | ParameterizedType type = (ParameterizedType) actual.getGenericSuperclass(); 116 | if (type.getRawType().equals(generic)) { 117 | Collections.addAll(values, ((ParameterizedType) type).getActualTypeArguments()); 118 | } 119 | } 120 | 121 | Map result = new HashMap(); 122 | for (int i = 0; i < names.size(); i++) { 123 | result.put(names.get(i), values.get(i)); 124 | } 125 | return result; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/Reflect.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | /* 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 17 | import java.lang.invoke.MethodHandles; 18 | import java.lang.reflect.AccessibleObject; 19 | import java.lang.reflect.Constructor; 20 | import java.lang.reflect.Field; 21 | import java.lang.reflect.InvocationHandler; 22 | import java.lang.reflect.Member; 23 | import java.lang.reflect.Method; 24 | import java.lang.reflect.Modifier; 25 | import java.lang.reflect.Proxy; 26 | import java.util.Arrays; 27 | import java.util.LinkedHashMap; 28 | import java.util.Map; 29 | 30 | /** 31 | * A wrapper for an {@link Object} or {@link Class} upon which reflective calls 32 | * can be made. 33 | *

34 | * An example of using Reflect is

 35 |  * // Static import all reflection methods to decrease verbosity
 36 |  * import static org.joor.Reflect.*;
 37 |  *
 38 |  * // Wrap an Object / Class / class name with the on() method:
 39 |  * on("java.lang.String")
 40 |  * // Invoke constructors using the create() method:
 41 |  * .create("Hello World")
 42 |  * // Invoke methods using the call() method:
 43 |  * .call("toString")
 44 |  * // Retrieve the wrapped object
 45 |  *
 46 |  * @author Lukas Eder
 47 |  * @author Irek Matysiewicz
 48 |  * @author Thomas Darimont
 49 |  */
 50 | public class Reflect {
 51 | 
 52 | 	// ---------------------------------------------------------------------
 53 | 	// Static API used as entrance points to the fluent API
 54 | 	// ---------------------------------------------------------------------
 55 | 
 56 | 	/**
 57 | 	 * Wrap a class name.
 58 | 	 * 

59 | * This is the same as calling on(Class.forName(name)) 60 | * 61 | * @param name A fully qualified class name 62 | * @return A wrapped class object, to be used for further reflection. 63 | * @throws ReflectException If any reflection exception occurred. 64 | * @see #on(Class) 65 | */ 66 | public static Reflect on(String name) throws ReflectException { 67 | return on(forName(name)); 68 | } 69 | 70 | /** 71 | * Wrap a class name, loading it via a given class loader. 72 | *

73 | * This is the same as calling 74 | * on(Class.forName(name, classLoader)) 75 | * 76 | * @param name A fully qualified class name. 77 | * @param classLoader The class loader in whose context the class should be 78 | * loaded. 79 | * @return A wrapped class object, to be used for further reflection. 80 | * @throws ReflectException If any reflection exception occurred. 81 | * @see #on(Class) 82 | */ 83 | public static Reflect on(String name, ClassLoader classLoader) throws ReflectException { 84 | return on(forName(name, classLoader)); 85 | } 86 | 87 | /** 88 | * Wrap a class. 89 | *

90 | * Use this when you want to access static fields and methods on a 91 | * {@link Class} object, or as a basis for constructing objects of that 92 | * class using {@link #create(Object...)} 93 | * 94 | * @param clazz The class to be wrapped 95 | * @return A wrapped class object, to be used for further reflection. 96 | */ 97 | public static Reflect on(Class clazz) { 98 | return new Reflect(clazz); 99 | } 100 | 101 | /** 102 | * Wrap an object. 103 | *

104 | * Use this when you want to access instance fields and methods on any 105 | * {@link Object} 106 | * 107 | * @param object The object to be wrapped 108 | * @return A wrapped object, to be used for further reflection. 109 | */ 110 | public static Reflect on(Object object) { 111 | return new Reflect(object); 112 | } 113 | 114 | /** 115 | * Conveniently render an {@link AccessibleObject} accessible. 116 | *

117 | * To prevent {@link SecurityException}, this is only done if the argument 118 | * object and its declaring class are non-public. 119 | * 120 | * @param accessible The object to render accessible 121 | * @return The argument object rendered accessible 122 | */ 123 | public static T accessible(T accessible) { 124 | if (accessible == null) { 125 | return null; 126 | } 127 | 128 | if (accessible instanceof Member) { 129 | Member member = (Member) accessible; 130 | 131 | if (Modifier.isPublic(member.getModifiers()) && 132 | Modifier.isPublic(member.getDeclaringClass().getModifiers())) { 133 | 134 | return accessible; 135 | } 136 | } 137 | 138 | // [jOOQ #3392] The accessible flag is set to false by default, also for public members. 139 | if (!accessible.isAccessible()) { 140 | accessible.setAccessible(true); 141 | } 142 | 143 | return accessible; 144 | } 145 | 146 | // --------------------------------------------------------------------- 147 | // Members 148 | // --------------------------------------------------------------------- 149 | 150 | /* [java-8] */ 151 | private static final Constructor CACHED_LOOKUP_CONSTRUCTOR; 152 | 153 | static { 154 | try { 155 | CACHED_LOOKUP_CONSTRUCTOR = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); 156 | 157 | if (!CACHED_LOOKUP_CONSTRUCTOR.isAccessible()) 158 | CACHED_LOOKUP_CONSTRUCTOR.setAccessible(true); 159 | } 160 | catch (Exception e) { 161 | throw new IllegalStateException(e); 162 | } 163 | } 164 | /* [/java-8] */ 165 | 166 | /** 167 | * The wrapped object 168 | */ 169 | private final Object object; 170 | 171 | /** 172 | * A flag indicating whether the wrapped object is a {@link Class} (for 173 | * accessing static fields and methods), or any other type of {@link Object} 174 | * (for accessing instance fields and methods). 175 | */ 176 | private final boolean isClass; 177 | 178 | // --------------------------------------------------------------------- 179 | // Constructors 180 | // --------------------------------------------------------------------- 181 | 182 | private Reflect(Class type) { 183 | this.object = type; 184 | this.isClass = true; 185 | } 186 | 187 | private Reflect(Object object) { 188 | this.object = object; 189 | this.isClass = false; 190 | } 191 | 192 | // --------------------------------------------------------------------- 193 | // Fluent Reflection API 194 | // --------------------------------------------------------------------- 195 | 196 | /** 197 | * Get the wrapped object 198 | * 199 | * @param A convenience generic parameter for automatic unsafe casting 200 | */ 201 | @SuppressWarnings("unchecked") 202 | public T get() { 203 | return (T) object; 204 | } 205 | 206 | /** 207 | * Set a field value. 208 | *

209 | * This is roughly equivalent to {@link Field#set(Object, Object)}. If the 210 | * wrapped object is a {@link Class}, then this will set a value to a static 211 | * member field. If the wrapped object is any other {@link Object}, then 212 | * this will set a value to an instance member field. 213 | *

214 | * This method is also capable of setting the value of (static) final 215 | * fields. This may be convenient in situations where no 216 | * {@link SecurityManager} is expected to prevent this, but do note that 217 | * (especially static) final fields may already have been inlined by the 218 | * javac and/or JIT and relevant code deleted from the runtime verison of 219 | * your program, so setting these fields might not have any effect on your 220 | * execution. 221 | *

222 | * For restrictions of usage regarding setting values on final fields check: 223 | * http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection 225 | * ... and http://pveentjer.blogspot.co.at/2017/01/final-static-boolean-jit.html 227 | * 228 | * @param name The field name 229 | * @param value The new field value 230 | * @return The same wrapped object, to be used for further reflection. 231 | * @throws ReflectException If any reflection exception occurred. 232 | */ 233 | public Reflect set(String name, Object value) throws ReflectException { 234 | try { 235 | Field field = field0(name); 236 | if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) { 237 | Field modifiersField = Field.class.getDeclaredField("modifiers"); 238 | modifiersField.setAccessible(true); 239 | modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 240 | } 241 | field.set(object, unwrap(value)); 242 | return this; 243 | } 244 | catch (Exception e) { 245 | throw new ReflectException(e); 246 | } 247 | } 248 | 249 | /** 250 | * Get a field value. 251 | *

252 | * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped 253 | * object is a {@link Class}, then this will get a value from a static 254 | * member field. If the wrapped object is any other {@link Object}, then 255 | * this will get a value from an instance member field. 256 | *

257 | * If you want to "navigate" to a wrapped version of the field, use 258 | * {@link #field(String)} instead. 259 | * 260 | * @param name The field name 261 | * @return The field value 262 | * @throws ReflectException If any reflection exception occurred. 263 | * @see #field(String) 264 | */ 265 | public T get(String name) throws ReflectException { 266 | return field(name).get(); 267 | } 268 | 269 | /** 270 | * Get a wrapped field. 271 | *

272 | * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped 273 | * object is a {@link Class}, then this will wrap a static member field. If 274 | * the wrapped object is any other {@link Object}, then this wrap an 275 | * instance member field. 276 | * 277 | * @param name The field name 278 | * @return The wrapped field 279 | * @throws ReflectException If any reflection exception occurred. 280 | */ 281 | public Reflect field(String name) throws ReflectException { 282 | try { 283 | Field field = field0(name); 284 | return on(field.get(object)); 285 | } 286 | catch (Exception e) { 287 | throw new ReflectException(e); 288 | } 289 | } 290 | 291 | private Field field0(String name) throws ReflectException { 292 | Class type = type(); 293 | 294 | // Try getting a public field 295 | try { 296 | return accessible(type.getField(name)); 297 | } 298 | 299 | // Try again, getting a non-public field 300 | catch (NoSuchFieldException e) { 301 | do { 302 | try { 303 | return accessible(type.getDeclaredField(name)); 304 | } 305 | catch (NoSuchFieldException ignore) {} 306 | 307 | type = type.getSuperclass(); 308 | } 309 | while (type != null); 310 | 311 | throw new ReflectException(e); 312 | } 313 | } 314 | 315 | /** 316 | * Get a Map containing field names and wrapped values for the fields' 317 | * values. 318 | *

319 | * If the wrapped object is a {@link Class}, then this will return static 320 | * fields. If the wrapped object is any other {@link Object}, then this will 321 | * return instance fields. 322 | *

323 | * These two calls are equivalent

324 | 	 * on(object).field("myField");
325 | 	 * on(object).fields().get("myField");
326 | 	 * 
327 | * 328 | * @return A map containing field names and wrapped values. 329 | */ 330 | public Map fields() { 331 | Map result = new LinkedHashMap(); 332 | Class type = type(); 333 | 334 | do { 335 | for (Field field : type.getDeclaredFields()) { 336 | if (!isClass ^ Modifier.isStatic(field.getModifiers())) { 337 | String name = field.getName(); 338 | 339 | if (!result.containsKey(name)) 340 | result.put(name, field(name)); 341 | } 342 | } 343 | 344 | type = type.getSuperclass(); 345 | } 346 | while (type != null); 347 | 348 | return result; 349 | } 350 | 351 | /** 352 | * Call a method by its name. 353 | *

354 | * This is a convenience method for calling 355 | * call(name, new Object[0]) 356 | * 357 | * @param name The method name 358 | * @return The wrapped method result or the same wrapped object if the 359 | * method returns void, to be used for further 360 | * reflection. 361 | * @throws ReflectException If any reflection exception occurred. 362 | * @see #call(String, Object...) 363 | */ 364 | public Reflect call(String name) throws ReflectException { 365 | return call(name, new Object[0]); 366 | } 367 | 368 | /** 369 | * Call a method by its name. 370 | *

371 | * This is roughly equivalent to {@link Method#invoke(Object, Object...)}. 372 | * If the wrapped object is a {@link Class}, then this will invoke a static 373 | * method. If the wrapped object is any other {@link Object}, then this will 374 | * invoke an instance method. 375 | *

376 | * Just like {@link Method#invoke(Object, Object...)}, this will try to wrap 377 | * primitive types or unwrap primitive type wrappers if applicable. If 378 | * several methods are applicable, by that rule, the first one encountered 379 | * is called. i.e. when calling

380 | 	 * on(...).call("method", 1, 1);
381 | 	 * 
The first of the following methods will be called: 382 | *
383 | 	 * public void method(int param1, Integer param2);
384 | 	 * public void method(Integer param1, int param2);
385 | 	 * public void method(Number param1, Number param2);
386 | 	 * public void method(Number param1, Object param2);
387 | 	 * public void method(int param1, Object param2);
388 | 	 * 
389 | *

390 | * The best matching method is searched for with the following strategy: 391 | *

    392 | *
  1. public method with exact signature match in class hierarchy
  2. 393 | *
  3. non-public method with exact signature match on declaring class
  4. 394 | *
  5. public method with similar signature in class hierarchy
  6. 395 | *
  7. non-public method with similar signature on declaring class
  8. 396 | *
397 | * 398 | * @param name The method name 399 | * @param args The method arguments 400 | * @return The wrapped method result or the same wrapped object if the 401 | * method returns void, to be used for further 402 | * reflection. 403 | * @throws ReflectException If any reflection exception occurred. 404 | */ 405 | public Reflect call(String name, Object... args) throws ReflectException { 406 | Class[] types = types(args); 407 | 408 | // Try invoking the "canonical" method, i.e. the one with exact 409 | // matching argument types 410 | try { 411 | Method method = exactMethod(name, types); 412 | return on(method, object, args); 413 | } 414 | 415 | // If there is no exact match, try to find a method that has a "similar" 416 | // signature if primitive argument types are converted to their wrappers 417 | catch (NoSuchMethodException e) { 418 | try { 419 | Method method = similarMethod(name, types); 420 | return on(method, object, args); 421 | } catch (NoSuchMethodException e1) { 422 | throw new ReflectException(e1); 423 | } 424 | } 425 | } 426 | 427 | /** 428 | * Searches a method with the exact same signature as desired. 429 | *

430 | * If a public method is found in the class hierarchy, this method is returned. 431 | * Otherwise a private method with the exact same signature is returned. 432 | * If no exact match could be found, we let the {@code NoSuchMethodException} pass through. 433 | */ 434 | private Method exactMethod(String name, Class[] types) throws NoSuchMethodException { 435 | Class type = type(); 436 | 437 | // first priority: find a public method with exact signature match in class hierarchy 438 | try { 439 | return type.getMethod(name, types); 440 | } 441 | 442 | // second priority: find a private method with exact signature match on declaring class 443 | catch (NoSuchMethodException e) { 444 | do { 445 | try { 446 | return type.getDeclaredMethod(name, types); 447 | } 448 | catch (NoSuchMethodException ignore) {} 449 | 450 | type = type.getSuperclass(); 451 | } 452 | while (type != null); 453 | 454 | throw new NoSuchMethodException(); 455 | } 456 | } 457 | 458 | /** 459 | * Searches a method with a similar signature as desired using 460 | * {@link #isSimilarSignature(java.lang.reflect.Method, String, Class[])}. 461 | *

462 | * First public methods are searched in the class hierarchy, then private 463 | * methods on the declaring class. If a method could be found, it is 464 | * returned, otherwise a {@code NoSuchMethodException} is thrown. 465 | */ 466 | private Method similarMethod(String name, Class[] types) throws NoSuchMethodException { 467 | Class type = type(); 468 | 469 | // first priority: find a public method with a "similar" signature in class hierarchy 470 | // similar interpreted in when primitive argument types are converted to their wrappers 471 | for (Method method : type.getMethods()) { 472 | if (isSimilarSignature(method, name, types)) { 473 | return method; 474 | } 475 | } 476 | 477 | // second priority: find a non-public method with a "similar" signature on declaring class 478 | do { 479 | for (Method method : type.getDeclaredMethods()) { 480 | if (isSimilarSignature(method, name, types)) { 481 | return method; 482 | } 483 | } 484 | 485 | type = type.getSuperclass(); 486 | } 487 | while (type != null); 488 | 489 | throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + "."); 490 | } 491 | 492 | /** 493 | * Determines if a method has a "similar" signature, especially if wrapping 494 | * primitive argument types would result in an exactly matching signature. 495 | */ 496 | private boolean isSimilarSignature(Method possiblyMatchingMethod, String desiredMethodName, Class[] desiredParamTypes) { 497 | return possiblyMatchingMethod.getName().equals(desiredMethodName) && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes); 498 | } 499 | 500 | /** 501 | * Call a constructor. 502 | *

503 | * This is a convenience method for calling 504 | * create(new Object[0]) 505 | * 506 | * @return The wrapped new object, to be used for further reflection. 507 | * @throws ReflectException If any reflection exception occurred. 508 | * @see #create(Object...) 509 | */ 510 | public Reflect create() throws ReflectException { 511 | return create(new Object[0]); 512 | } 513 | 514 | /** 515 | * Call a constructor. 516 | *

517 | * This is roughly equivalent to {@link Constructor#newInstance(Object...)}. 518 | * If the wrapped object is a {@link Class}, then this will create a new 519 | * object of that class. If the wrapped object is any other {@link Object}, 520 | * then this will create a new object of the same type. 521 | *

522 | * Just like {@link Constructor#newInstance(Object...)}, this will try to 523 | * wrap primitive types or unwrap primitive type wrappers if applicable. If 524 | * several constructors are applicable, by that rule, the first one 525 | * encountered is called. i.e. when calling

526 | 	 * on(C.class).create(1, 1);
527 | 	 * 
The first of the following constructors will be applied: 528 | *
529 | 	 * public C(int param1, Integer param2);
530 | 	 * public C(Integer param1, int param2);
531 | 	 * public C(Number param1, Number param2);
532 | 	 * public C(Number param1, Object param2);
533 | 	 * public C(int param1, Object param2);
534 | 	 * 
535 | * 536 | * @param args The constructor arguments 537 | * @return The wrapped new object, to be used for further reflection. 538 | * @throws ReflectException If any reflection exception occurred. 539 | */ 540 | public Reflect create(Object... args) throws ReflectException { 541 | Class[] types = types(args); 542 | 543 | // Try invoking the "canonical" constructor, i.e. the one with exact 544 | // matching argument types 545 | try { 546 | Constructor constructor = type().getDeclaredConstructor(types); 547 | return on(constructor, args); 548 | } 549 | 550 | // If there is no exact match, try to find one that has a "similar" 551 | // signature if primitive argument types are converted to their wrappers 552 | catch (NoSuchMethodException e) { 553 | for (Constructor constructor : type().getDeclaredConstructors()) { 554 | if (match(constructor.getParameterTypes(), types)) { 555 | return on(constructor, args); 556 | } 557 | } 558 | 559 | throw new ReflectException(e); 560 | } 561 | } 562 | 563 | /** 564 | * Create a proxy for the wrapped object allowing to typesafely invoke 565 | * methods on it using a custom interface 566 | * 567 | * @param proxyType The interface type that is implemented by the proxy 568 | * @return A proxy for the wrapped object 569 | */ 570 | @SuppressWarnings("unchecked") 571 | public

P as(final Class

proxyType) { 572 | final boolean isMap = (object instanceof Map); 573 | final InvocationHandler handler = new InvocationHandler() { 574 | @SuppressWarnings("null") 575 | @Override 576 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 577 | String name = method.getName(); 578 | 579 | // Actual method name matches always come first 580 | try { 581 | return on(object).call(name, args).get(); 582 | } 583 | 584 | // [#14] Emulate POJO behaviour on wrapped map objects 585 | catch (ReflectException e) { 586 | if (isMap) { 587 | Map map = (Map) object; 588 | int length = (args == null ? 0 : args.length); 589 | 590 | if (length == 0 && name.startsWith("get")) { 591 | return map.get(property(name.substring(3))); 592 | } 593 | else if (length == 0 && name.startsWith("is")) { 594 | return map.get(property(name.substring(2))); 595 | } 596 | else if (length == 1 && name.startsWith("set")) { 597 | map.put(property(name.substring(3)), args[0]); 598 | return null; 599 | } 600 | } 601 | 602 | /* [java-8] */ 603 | if (method.isDefault()) { 604 | return CACHED_LOOKUP_CONSTRUCTOR 605 | .newInstance(proxyType) 606 | .unreflectSpecial(method, proxyType) 607 | .bindTo(proxy) 608 | .invokeWithArguments(args); 609 | } 610 | /* [/java-8] */ 611 | 612 | throw e; 613 | } 614 | } 615 | }; 616 | 617 | return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[] { proxyType }, handler); 618 | } 619 | 620 | /** 621 | * Get the POJO property name of an getter/setter 622 | */ 623 | private static String property(String string) { 624 | int length = string.length(); 625 | 626 | if (length == 0) { 627 | return ""; 628 | } 629 | else if (length == 1) { 630 | return string.toLowerCase(); 631 | } 632 | else { 633 | return string.substring(0, 1).toLowerCase() + string.substring(1); 634 | } 635 | } 636 | 637 | // --------------------------------------------------------------------- 638 | // Object API 639 | // --------------------------------------------------------------------- 640 | 641 | /** 642 | * Check whether two arrays of types match, converting primitive types to 643 | * their corresponding wrappers. 644 | */ 645 | private boolean match(Class[] declaredTypes, Class[] actualTypes) { 646 | if (declaredTypes.length == actualTypes.length) { 647 | for (int i = 0; i < actualTypes.length; i++) { 648 | if (actualTypes[i] == NULL.class) 649 | continue; 650 | 651 | if (wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) 652 | continue; 653 | 654 | return false; 655 | } 656 | 657 | return true; 658 | } 659 | else { 660 | return false; 661 | } 662 | } 663 | 664 | /** 665 | * {@inheritDoc} 666 | */ 667 | @Override 668 | public int hashCode() { 669 | return object.hashCode(); 670 | } 671 | 672 | /** 673 | * {@inheritDoc} 674 | */ 675 | @Override 676 | public boolean equals(Object obj) { 677 | if (obj instanceof Reflect) { 678 | return object.equals(((Reflect) obj).get()); 679 | } 680 | 681 | return false; 682 | } 683 | 684 | /** 685 | * {@inheritDoc} 686 | */ 687 | @Override 688 | public String toString() { 689 | return object.toString(); 690 | } 691 | 692 | // --------------------------------------------------------------------- 693 | // Utility methods 694 | // --------------------------------------------------------------------- 695 | 696 | /** 697 | * Wrap an object created from a constructor 698 | */ 699 | private static Reflect on(Constructor constructor, Object... args) throws ReflectException { 700 | try { 701 | return on(accessible(constructor).newInstance(args)); 702 | } 703 | catch (Exception e) { 704 | throw new ReflectException(e); 705 | } 706 | } 707 | 708 | /** 709 | * Wrap an object returned from a method 710 | */ 711 | private static Reflect on(Method method, Object object, Object... args) throws ReflectException { 712 | try { 713 | accessible(method); 714 | 715 | if (method.getReturnType() == void.class) { 716 | method.invoke(object, args); 717 | return on(object); 718 | } 719 | else { 720 | return on(method.invoke(object, args)); 721 | } 722 | } 723 | catch (Exception e) { 724 | throw new ReflectException(e); 725 | } 726 | } 727 | 728 | /** 729 | * Unwrap an object 730 | */ 731 | private static Object unwrap(Object object) { 732 | if (object instanceof Reflect) { 733 | return ((Reflect) object).get(); 734 | } 735 | 736 | return object; 737 | } 738 | 739 | /** 740 | * Get an array of types for an array of objects 741 | * 742 | * @see Object#getClass() 743 | */ 744 | private static Class[] types(Object... values) { 745 | if (values == null) { 746 | return new Class[0]; 747 | } 748 | 749 | Class[] result = new Class[values.length]; 750 | 751 | for (int i = 0; i < values.length; i++) { 752 | Object value = values[i]; 753 | result[i] = value == null ? NULL.class : value.getClass(); 754 | } 755 | 756 | return result; 757 | } 758 | 759 | /** 760 | * Load a class 761 | * 762 | * @see Class#forName(String) 763 | */ 764 | private static Class forName(String name) throws ReflectException { 765 | try { 766 | return Class.forName(name); 767 | } 768 | catch (Exception e) { 769 | throw new ReflectException(e); 770 | } 771 | } 772 | 773 | private static Class forName(String name, ClassLoader classLoader) throws ReflectException { 774 | try { 775 | return Class.forName(name, true, classLoader); 776 | } 777 | catch (Exception e) { 778 | throw new ReflectException(e); 779 | } 780 | } 781 | 782 | /** 783 | * Get the type of the wrapped object. 784 | * 785 | * @see Object#getClass() 786 | */ 787 | public Class type() { 788 | if (isClass) { 789 | return (Class) object; 790 | } 791 | else { 792 | return object.getClass(); 793 | } 794 | } 795 | 796 | /** 797 | * Get a wrapper type for a primitive type, or the argument type itself, if 798 | * it is not a primitive type. 799 | */ 800 | public static Class wrapper(Class type) { 801 | if (type == null) { 802 | return null; 803 | } 804 | else if (type.isPrimitive()) { 805 | if (boolean.class == type) { 806 | return Boolean.class; 807 | } 808 | else if (int.class == type) { 809 | return Integer.class; 810 | } 811 | else if (long.class == type) { 812 | return Long.class; 813 | } 814 | else if (short.class == type) { 815 | return Short.class; 816 | } 817 | else if (byte.class == type) { 818 | return Byte.class; 819 | } 820 | else if (double.class == type) { 821 | return Double.class; 822 | } 823 | else if (float.class == type) { 824 | return Float.class; 825 | } 826 | else if (char.class == type) { 827 | return Character.class; 828 | } 829 | else if (void.class == type) { 830 | return Void.class; 831 | } 832 | } 833 | 834 | return type; 835 | } 836 | 837 | private static class NULL {} 838 | 839 | } 840 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/ReflectException.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | /** 4 | * A unchecked wrapper for any of Java's checked reflection exceptions: 5 | *

6 | * These exceptions are 7 | *

    8 | *
  • {@link ClassNotFoundException}
  • 9 | *
  • {@link IllegalAccessException}
  • 10 | *
  • {@link IllegalArgumentException}
  • 11 | *
  • {@link InstantiationException}
  • 12 | *
  • {@link NoSuchMethodException}
  • 13 | *
  • {@link NoSuchFieldException}
  • 14 | *
  • {@link SecurityException}
  • 15 | *
16 | * 17 | * @author Lukas Eder 18 | */ 19 | public class ReflectException extends RuntimeException { 20 | 21 | /** 22 | * Generated UID 23 | */ 24 | private static final long serialVersionUID = -6213149635297151442L; 25 | 26 | public ReflectException(String message) { 27 | super(message); 28 | } 29 | 30 | public ReflectException(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public ReflectException() { 35 | super(); 36 | } 37 | 38 | public ReflectException(Throwable cause) { 39 | super(cause); 40 | } 41 | } -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/Reflections.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.Modifier; 7 | import java.lang.reflect.ParameterizedType; 8 | import java.lang.reflect.Type; 9 | 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.apache.commons.lang3.Validate; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * 反射工具类. 17 | * 18 | * 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. 19 | * 20 | */ 21 | public class Reflections { 22 | private static final String SETTER_PREFIX = "set"; 23 | 24 | private static final String GETTER_PREFIX = "get"; 25 | 26 | private static final String CGLIB_CLASS_SEPARATOR = "$$"; 27 | 28 | private static Logger logger = LoggerFactory.getLogger(Reflections.class); 29 | 30 | /** 31 | * 调用Getter方法. 32 | */ 33 | public static Object invokeGetter(Object obj, String propertyName) { 34 | String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(propertyName); 35 | return invokeMethod(obj, getterMethodName, new Class[] {}, new Object[] {}); 36 | } 37 | 38 | /** 39 | * 调用Setter方法, 仅匹配方法名。 40 | */ 41 | public static void invokeSetter(Object obj, String propertyName, Object value) { 42 | String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(propertyName); 43 | invokeMethodByName(obj, setterMethodName, new Object[] { value }); 44 | } 45 | 46 | /** 47 | * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. 48 | */ 49 | public static Object getFieldValue(final Object obj, final String fieldName) { 50 | Field field = getAccessibleField(obj, fieldName); 51 | 52 | if (field == null) { 53 | throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]"); 54 | } 55 | 56 | Object result = null; 57 | try { 58 | result = field.get(obj); 59 | } catch (IllegalAccessException e) { 60 | logger.error("不可能抛出的异常{}", e.getMessage()); 61 | } 62 | return result; 63 | } 64 | 65 | /** 66 | * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. 67 | */ 68 | public static void setFieldValue(final Object obj, final String fieldName, final Object value) { 69 | Field field = getAccessibleField(obj, fieldName); 70 | 71 | if (field == null) { 72 | throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]"); 73 | } 74 | 75 | try { 76 | field.set(obj, value); 77 | } catch (IllegalAccessException e) { 78 | logger.error("不可能抛出的异常:{}", e.getMessage()); 79 | } 80 | } 81 | 82 | /** 83 | * 直接调用对象方法, 无视private/protected修饰符. 84 | * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. 85 | * 同时匹配方法名+参数类型, 86 | */ 87 | public static Object invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, 88 | final Object[] args) { 89 | Method method = getAccessibleMethod(obj, methodName, parameterTypes); 90 | if (method == null) { 91 | throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); 92 | } 93 | 94 | try { 95 | return method.invoke(obj, args); 96 | } catch (Exception e) { 97 | throw convertReflectionExceptionToUnchecked(e); 98 | } 99 | } 100 | 101 | /** 102 | * 直接调用对象方法, 无视private/protected修饰符, 103 | * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. 104 | * 只匹配函数名,如果有多个同名函数调用第一个。 105 | */ 106 | public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) { 107 | Method method = getAccessibleMethodByName(obj, methodName); 108 | if (method == null) { 109 | throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); 110 | } 111 | 112 | try { 113 | return method.invoke(obj, args); 114 | } catch (Exception e) { 115 | throw convertReflectionExceptionToUnchecked(e); 116 | } 117 | } 118 | 119 | /** 120 | * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. 121 | * 122 | * 如向上转型到Object仍无法找到, 返回null. 123 | */ 124 | public static Field getAccessibleField(final Object obj, final String fieldName) { 125 | Validate.notNull(obj, "object can't be null"); 126 | Validate.notBlank(fieldName, "fieldName can't be blank"); 127 | for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) { 128 | try { 129 | Field field = superClass.getDeclaredField(fieldName); 130 | makeAccessible(field); 131 | return field; 132 | } catch (NoSuchFieldException e) {//NOSONAR 133 | // Field不在当前类定义,继续向上转型 134 | } 135 | } 136 | return null; 137 | } 138 | 139 | /** 140 | * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 141 | * 如向上转型到Object仍无法找到, 返回null. 142 | * 匹配函数名+参数类型。 143 | * 144 | * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) 145 | */ 146 | public static Method getAccessibleMethod(final Object obj, final String methodName, 147 | final Class... parameterTypes) { 148 | Validate.notNull(obj, "object can't be null"); 149 | Validate.notBlank(methodName, "methodName can't be blank"); 150 | 151 | for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { 152 | try { 153 | Method method = searchType.getDeclaredMethod(methodName, parameterTypes); 154 | makeAccessible(method); 155 | return method; 156 | } catch (NoSuchMethodException e) { 157 | // Method不在当前类定义,继续向上转型 158 | } 159 | } 160 | return null; 161 | } 162 | 163 | /** 164 | * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 165 | * 如向上转型到Object仍无法找到, 返回null. 166 | * 只匹配函数名。 167 | * 168 | * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) 169 | */ 170 | public static Method getAccessibleMethodByName(final Object obj, final String methodName) { 171 | Validate.notNull(obj, "object can't be null"); 172 | Validate.notBlank(methodName, "methodName can't be blank"); 173 | 174 | for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { 175 | Method[] methods = searchType.getDeclaredMethods(); 176 | for (Method method : methods) { 177 | if (method.getName().equals(methodName)) { 178 | makeAccessible(method); 179 | return method; 180 | } 181 | } 182 | } 183 | return null; 184 | } 185 | 186 | /** 187 | * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 188 | */ 189 | public static void makeAccessible(Method method) { 190 | if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) 191 | && !method.isAccessible()) { 192 | method.setAccessible(true); 193 | } 194 | } 195 | 196 | /** 197 | * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 198 | */ 199 | public static void makeAccessible(Field field) { 200 | if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier 201 | .isFinal(field.getModifiers())) && !field.isAccessible()) { 202 | field.setAccessible(true); 203 | } 204 | } 205 | 206 | /** 207 | * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 208 | * 如无法找到, 返回Object.class. 209 | * eg. 210 | * public UserDao extends HibernateDao 211 | * 212 | * @param clazz The class to introspect 213 | * @return the first generic declaration, or Object.class if cannot be determined 214 | */ 215 | public static Class getClassGenricType(final Class clazz) { 216 | return getClassGenricType(clazz, 0); 217 | } 218 | 219 | /** 220 | * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 221 | * 如无法找到, 返回Object.class. 222 | * 223 | * 如public UserDao extends HibernateDao 224 | * 225 | * @param clazz clazz The class to introspect 226 | * @param index the Index of the generic ddeclaration,start from 0. 227 | * @return the index generic declaration, or Object.class if cannot be determined 228 | */ 229 | public static Class getClassGenricType(final Class clazz, final int index) { 230 | 231 | Type genType = clazz.getGenericSuperclass(); 232 | 233 | if (!(genType instanceof ParameterizedType)) { 234 | logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType"); 235 | return Object.class; 236 | } 237 | 238 | Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); 239 | 240 | if (index >= params.length || index < 0) { 241 | logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " 242 | + params.length); 243 | return Object.class; 244 | } 245 | if (!(params[index] instanceof Class)) { 246 | logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); 247 | return Object.class; 248 | } 249 | 250 | return (Class) params[index]; 251 | } 252 | 253 | public static Class getUserClass(Object instance) { 254 | Class clazz = instance.getClass(); 255 | if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { 256 | Class superClass = clazz.getSuperclass(); 257 | if (superClass != null && !Object.class.equals(superClass)) { 258 | return superClass; 259 | } 260 | } 261 | return clazz; 262 | 263 | } 264 | 265 | /** 266 | * 将反射时的checked exception转换为unchecked exception. 267 | */ 268 | public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) { 269 | if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException 270 | || e instanceof NoSuchMethodException) { 271 | return new IllegalArgumentException(e); 272 | } else if (e instanceof InvocationTargetException) { 273 | return new RuntimeException(((InvocationTargetException) e).getTargetException()); 274 | } else if (e instanceof RuntimeException) { 275 | return (RuntimeException) e; 276 | } 277 | return new RuntimeException("Unexpected Checked Exception.", e); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/ResponseVo.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | /** 4 | * @author lichangchao 5 | * @功能 返回json格式数据 6 | * 7 | */ 8 | public class ResponseVo { 9 | private static final long serialVersionUID = -3819569459544701549L; 10 | private Integer code; 11 | private String desc; 12 | private Object data; 13 | 14 | private ResponseVo() { 15 | } 16 | 17 | public Integer getCode() { 18 | return this.code; 19 | } 20 | 21 | public ResponseVo setCode(Integer code) { 22 | this.code = code; 23 | return this; 24 | } 25 | 26 | public String getDesc() { 27 | return this.desc; 28 | } 29 | 30 | public ResponseVo setDesc(String desc) { 31 | this.desc = desc; 32 | return this; 33 | } 34 | 35 | public Object getData() { 36 | return this.data; 37 | } 38 | 39 | public ResponseVo setData(Object data) { 40 | this.data = data; 41 | return this; 42 | } 43 | 44 | public static ResponseVo BUILDER() { 45 | return new ResponseVo(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/ResponseVoUtil.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import com.lccf.enums.EResultCode; 4 | 5 | /** 6 | * @author lichangchao 7 | * @功能 返回json格式数据 8 | * 9 | */ 10 | public class ResponseVoUtil { 11 | /** 统一失败成功返回码 **/ 12 | public static ResponseVo failResult(String message) { 13 | return ResponseVo.BUILDER().setCode(EResultCode.FAIL.getKey()).setDesc(message); 14 | } 15 | 16 | public static ResponseVo failResult(String Message, Object b) { 17 | return ResponseVo.BUILDER().setCode(EResultCode.FAIL.getKey()).setDesc(Message).setData(b); 18 | } 19 | 20 | public static ResponseVo successMsg(String Message) { 21 | return ResponseVo.BUILDER().setCode(EResultCode.SUCCESS.getKey()).setDesc(Message); 22 | } 23 | 24 | public static ResponseVo successData(Object data) { 25 | return ResponseVo.BUILDER().setCode(EResultCode.SUCCESS.getKey()).setData(data); 26 | } 27 | 28 | public static ResponseVo successResult(String Message, Object data) { 29 | return ResponseVo.BUILDER().setCode(EResultCode.SUCCESS.getKey()).setData(data).setDesc(Message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/SpringUtil.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | 9 | /** 10 | * 普通类调用Spring bean对象: 11 | *

12 | * 说明: 13 | *

14 | * 1、此类需要放到App.java同包或者子包下才能被扫描,否则失效。 15 | * 16 | * @author Administrator 17 | */ 18 | 19 | 20 | public class SpringUtil implements ApplicationContextAware { 21 | 22 | private static ApplicationContext applicationContext = null; 23 | 24 | 25 | @Override 26 | 27 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 28 | 29 | if (SpringUtil.applicationContext == null) { 30 | 31 | SpringUtil.applicationContext = applicationContext; 32 | 33 | } 34 | 35 | } 36 | 37 | 38 | //获取applicationContext 39 | 40 | public static ApplicationContext getApplicationContext() { 41 | 42 | return applicationContext; 43 | 44 | } 45 | 46 | 47 | //通过name获取 Bean. 48 | 49 | public static Object getBean(String name) { 50 | 51 | return getApplicationContext().getBean(name); 52 | 53 | } 54 | 55 | 56 | //通过class获取Bean. 57 | 58 | public static T getBean(Class clazz) { 59 | 60 | return getApplicationContext().getBean(clazz); 61 | 62 | } 63 | 64 | 65 | //通过name,以及Clazz返回指定的Bean 66 | 67 | public static T getBean(String name, Class clazz) { 68 | 69 | return getApplicationContext().getBean(name, clazz); 70 | 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * 7 | * 8 | * @author lichangchao 9 | * @version 1.0.0 10 | * @date 2017/12/4 17:30 11 | * @see 12 | */ 13 | public class StringUtil { 14 | 15 | public static String transContent(String content,String args){ 16 | if(StringUtils.isEmpty(content))return ""; 17 | if(StringUtils.isEmpty(args))return content; 18 | String[] arg = args.split(","); 19 | for (int i=0 ;i list, OutputStream output, String encoding) { 26 | Class clazz = Reflections.getUserClass(list.get(0)); 27 | List fieldList = ExcelUtil.getMappedFiled(clazz, null); 28 | Field[] fields = new Field[fieldList.size()]; 29 | for(Field f : fieldList) { 30 | ExcelVOAttribute attr = f 31 | .getAnnotation(ExcelVOAttribute.class); 32 | int col = ExcelUtil.getExcelCol(attr.column()); 33 | fields[col] = f; 34 | } 35 | 36 | //写入表头 37 | ExcelVO excelVo = (ExcelVO) clazz.getAnnotation(ExcelVO.class); 38 | String[] tips = excelVo.tips(); 39 | StringBuilder sb = new StringBuilder(); 40 | if(tips.length > 0) { 41 | String tip = StringUtils.join(tips, ","); 42 | sb.append(tip).append("\r\n"); 43 | } 44 | 45 | //append header 46 | for(Field f : fields) { 47 | ExcelVOAttribute attr = f.getAnnotation(ExcelVOAttribute.class); 48 | sb.append(attr.name()).append(","); 49 | } 50 | sb.deleteCharAt(sb.length() - 1).append("\r\n"); 51 | 52 | //写入所有行 53 | for(Object o : list) { 54 | for(Field f : fields) { 55 | ExcelVOAttribute attr = f.getAnnotation(ExcelVOAttribute.class); 56 | String appendValue = ""; 57 | 58 | if(attr.isExport()) { 59 | //这里只判定了Date 类型 数值类型的 未作判定 60 | Object value = Reflections.getFieldValue(o, f.getName()); 61 | if(f.getType() == Date.class) { 62 | appendValue = TimeUtil.format((Date)value, attr.dateFormat()); 63 | } else { 64 | appendValue = value == null ? "" : String.valueOf(value);// 如果数据存在就填入,不存在填入空格. 65 | } 66 | } 67 | sb.append(appendValue).append(","); 68 | } 69 | sb.deleteCharAt(sb.length() - 1).append("\r\n"); 70 | } 71 | 72 | try { 73 | output.write(sb.toString().getBytes(encoding)); 74 | output.flush(); 75 | return true; 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | return false; 79 | } finally { 80 | IOUtils.closeQuietly(output); 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/excel/ExcelUtil.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util.excel; 2 | 3 | import com.lccf.util.TimeUtil; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.io.PushbackInputStream; 8 | import java.lang.reflect.Field; 9 | import java.text.DecimalFormat; 10 | import java.text.SimpleDateFormat; 11 | import java.util.ArrayList; 12 | import java.util.Date; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | import org.apache.commons.beanutils.BeanUtils; 20 | import org.apache.commons.lang3.StringUtils; 21 | import org.apache.poi.POIXMLDocument; 22 | import org.apache.poi.hssf.usermodel.HSSFCell; 23 | import org.apache.poi.hssf.usermodel.HSSFDataFormat; 24 | import org.apache.poi.hssf.usermodel.HSSFDateUtil; 25 | import org.apache.poi.hssf.usermodel.HSSFHeader; 26 | import org.apache.poi.hssf.usermodel.HSSFRow; 27 | import org.apache.poi.hssf.usermodel.HSSFSheet; 28 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; 29 | import org.apache.poi.hssf.util.HSSFColor; 30 | import org.apache.poi.openxml4j.opc.OPCPackage; 31 | import org.apache.poi.poifs.filesystem.POIFSFileSystem; 32 | import org.apache.poi.ss.usermodel.Cell; 33 | import org.apache.poi.ss.usermodel.CellStyle; 34 | import org.apache.poi.ss.usermodel.Row; 35 | import org.apache.poi.ss.usermodel.Sheet; 36 | import org.apache.poi.ss.usermodel.Workbook; 37 | import org.apache.poi.util.IOUtils; 38 | import org.apache.poi.xssf.streaming.SXSSFSheet; 39 | import org.apache.poi.xssf.streaming.SXSSFWorkbook; 40 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 41 | 42 | 43 | 44 | /* 45 | * ExcelUtil工具类实现功能: 导出时传入list,即可实现导出为一个excel,其中每个对象T为Excel中的一条记录. 46 | * 导入时读取excel,得到的结果是一个list.T是自己定义的对象. 47 | * 需要导出的实体对象只需简单配置注解就能实现灵活导出,通过注解您可以方便实现下面功能: 1.实体属性配置了注解就能导出到excel中,每个属性都对应一列. 48 | * 2.列名称可以通过注解配置. 3.导出到哪一列可以通过注解配置. 4.鼠标移动到该列时提示信息可以通过注解配置. 49 | * 5.用注解设置只能下拉选择不能随意填写功能. 6.用注解设置是否只导出标题而不导出内容,这在导出内容作为模板以供用户填写时比较实用. 50 | * 本工具类以后可能还会加功能,请关注我的博客: http://blog.csdn.net/lk_blog 51 | * 52 | * TODO: 增加JSR303 验证数据准确性 返回Map> 53 | */ 54 | public class ExcelUtil { 55 | Class clazz; 56 | 57 | public ExcelUtil(Class clazz) { 58 | this.clazz = clazz; 59 | } 60 | 61 | public static Workbook create(InputStream in) throws IOException, org.apache.poi.openxml4j.exceptions.InvalidFormatException { 62 | if (!in.markSupported()) { 63 | in = new PushbackInputStream(in, 8); 64 | } 65 | if (POIFSFileSystem.hasPOIFSHeader(in)) { 66 | return new HSSFWorkbook(in); 67 | } 68 | if (POIXMLDocument.hasOOXMLHeader(in)) { 69 | return new XSSFWorkbook(OPCPackage.open(in)); 70 | } 71 | throw new IllegalArgumentException("你的excel版本目前poi解析不了"); 72 | } 73 | 74 | /** 75 | * 导入excel we don't command big data import using excel 76 | * TODO:类型设定改用common-utils 77 | * 78 | * @throws IOException 79 | */ 80 | public Map importExcel(String sheetName, InputStream input) throws Exception { 81 | int maxCol = 0; 82 | Map importMap = new HashMap<>(); 83 | List successList = new ArrayList(); 84 | List failList = new ArrayList(); 85 | Workbook workbook = null; 86 | try { 87 | workbook = create(input); 88 | Sheet sheet = workbook.getSheet(sheetName); 89 | if (!sheetName.trim().equals("")) { 90 | sheet = workbook.getSheet(sheetName);// 如果指定sheet名,则取指定sheet中的内容. 91 | } 92 | if (sheet == null) { 93 | sheet = workbook.getSheetAt(0); // 如果传入的sheet名不存在则默认指向第1个sheet. 94 | } 95 | int rows = sheet.getPhysicalNumberOfRows(); 96 | ExcelVO anno = clazz.getAnnotation(ExcelVO.class); 97 | int minRows = anno == null ? 0 : anno.skipLine(); 98 | if (rows > minRows) {// 有数据时才处理 99 | // Field[] allFields = clazz.getDeclaredFields();// 得到类的所有field. 100 | List allFields = getMappedFiled(clazz, null); 101 | 102 | Map fieldsMap = new HashMap();// 定义一个map用于存放列的序号和field. 103 | for (Field field : allFields) { 104 | // 将有注解的field存放到map中. 105 | if (field.isAnnotationPresent(ExcelVOAttribute.class)) { 106 | ExcelVOAttribute attr = field.getAnnotation(ExcelVOAttribute.class); 107 | int col = getExcelCol(attr.column());// 获得列号 108 | maxCol = Math.max(col, maxCol); 109 | // System.out.println(col + "====" + field.getName()); 110 | field.setAccessible(true);// 设置类的私有字段属性可访问. 111 | fieldsMap.put(col, field); 112 | } 113 | } 114 | for (int i = minRows; i < rows; i++) { 115 | Row row = sheet.getRow(i); 116 | // int cellNum = row.getPhysicalNumberOfCells(); 117 | // int cellNum = row.getLastCellNum(); 118 | int cellNum = maxCol; 119 | T entity = null; 120 | for (int j = 0; j <= cellNum; j++) { 121 | Cell cell = row.getCell(j); 122 | 123 | if (cell == null) { 124 | continue; 125 | } 126 | Field field = fieldsMap.get(j);// 从map中得到对应列的field. 127 | if (field == null) { 128 | continue; 129 | } 130 | ExcelVOAttribute voAttrAnn = field.getAnnotation(ExcelVOAttribute.class); 131 | String c = parseExcel(cell, voAttrAnn.decimalLength()); 132 | 133 | if (c == null || c.equals("")) { 134 | continue; 135 | } 136 | entity = (entity == null ? clazz.newInstance() : entity);// 如果不存在实例则新建. 137 | 138 | // 取得类型,并根据对象类型设置值. 139 | Class fieldType = field.getType(); 140 | if (String.class == fieldType) { 141 | field.set(entity, String.valueOf(c)); 142 | } else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) { 143 | field.set(entity, Integer.parseInt(c)); 144 | } else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) { 145 | field.set(entity, Long.valueOf(c)); 146 | } else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) { 147 | field.set(entity, Float.valueOf(c)); 148 | } else if ((Short.TYPE == fieldType) || (Short.class == fieldType)) { 149 | field.set(entity, Short.valueOf(c)); 150 | } else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) { 151 | field.set(entity, Double.valueOf(c)); 152 | } else if (Character.TYPE == fieldType) { 153 | if ((c != null) && (c.length() > 0)) { 154 | field.set(entity, Character.valueOf(c.charAt(0))); 155 | } 156 | } else if (Date.class == fieldType) { 157 | if ((c != null) && (c.length() > 0)) { 158 | field.set(entity, TimeUtil.parse(c)); 159 | } 160 | } 161 | 162 | ResJson resJson = validField(field, c); 163 | // 如果验证不成功则设置错误信息 164 | if (!resJson.isSuccess()) { 165 | BeanUtils.copyProperty(entity, "errorMessage", resJson.getMessage()); 166 | } 167 | } 168 | 169 | if (entity != null) { 170 | String errorMessage = BeanUtils.getProperty(entity, "errorMessage"); 171 | 172 | if (StringUtils.isEmpty(errorMessage)) { 173 | successList.add(entity); 174 | 175 | } else { 176 | failList.add(entity); 177 | } 178 | } 179 | } 180 | } 181 | importMap.put(true, successList); 182 | importMap.put(false, failList); 183 | } finally { 184 | IOUtils.closeQuietly(workbook); 185 | } 186 | 187 | return importMap; 188 | } 189 | 190 | /** 191 | * 处理cell中特殊的数据类型 特别是日期类型 192 | * 193 | * @param cell 194 | * @return 195 | */ 196 | private String parseExcel(Cell cell, int decimalLength) { 197 | String result = new String(); 198 | switch (cell.getCellType()) { 199 | case HSSFCell.CELL_TYPE_NUMERIC:// 数字类型 200 | if (HSSFDateUtil.isCellDateFormatted(cell)) {// 处理日期格式、时间格式 201 | SimpleDateFormat sdf = null; 202 | if (cell.getCellStyle().getDataFormat() == HSSFDataFormat.getBuiltinFormat("h:mm")) { 203 | sdf = new SimpleDateFormat("HH:mm"); 204 | } else {// 日期 205 | sdf = new SimpleDateFormat("yyyy-MM-dd"); 206 | } 207 | Date date = cell.getDateCellValue(); 208 | result = sdf.format(date); 209 | } else if (cell.getCellStyle().getDataFormat() == 58) { 210 | // 处理自定义日期格式:m月d日(通过判断单元格的格式id解决,id的值是58) 211 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 212 | double value = cell.getNumericCellValue(); 213 | Date date = org.apache.poi.ss.usermodel.DateUtil.getJavaDate(value); 214 | result = sdf.format(date); 215 | } else { 216 | double value = cell.getNumericCellValue(); 217 | CellStyle style = cell.getCellStyle(); 218 | DecimalFormat format = new DecimalFormat(); 219 | String temp = style.getDataFormatString(); 220 | // 单元格设置成常规 221 | if (temp.equals("General") || temp.contains("@")) { 222 | String pattern = decidePattern(decimalLength); 223 | format.applyPattern(pattern); 224 | } 225 | result = format.format(value); 226 | } 227 | break; 228 | case HSSFCell.CELL_TYPE_STRING:// String类型 229 | result = cell.getRichStringCellValue().toString(); 230 | break; 231 | case HSSFCell.CELL_TYPE_BLANK: 232 | result = ""; 233 | default: 234 | result = ""; 235 | break; 236 | } 237 | return result == null?null:result.trim(); 238 | } 239 | 240 | private String decidePattern(int decimalLength) { 241 | StringBuilder sb = new StringBuilder(decimalLength > 0 ? "#." : "#"); 242 | int length = decimalLength; 243 | while (length-- > 0) { 244 | sb.append("#"); 245 | } 246 | 247 | return sb.toString(); 248 | } 249 | 250 | /** 251 | * 对list数据源将其里面的数据导入到excel表单 252 | * 253 | * @param sheetName 工作表的名称 254 | * @param output java输出流 255 | */ 256 | public boolean exportExcel(List lists[], String sheetNames[], OutputStream output) { 257 | if (lists.length != sheetNames.length) { 258 | System.out.println("数组长度不一致"); 259 | return false; 260 | } 261 | 262 | SXSSFWorkbook workbook = new SXSSFWorkbook(500);// 产生工作薄对象 263 | 264 | for (int ii = 0; ii < lists.length; ii++) { 265 | List list = lists[ii]; 266 | String sheetName = sheetNames[ii]; 267 | 268 | List fields = getMappedFiled(clazz, null); 269 | 270 | SXSSFSheet sheet = workbook.createSheet();// 产生工作表对象 271 | 272 | workbook.setSheetName(ii, sheetName); 273 | 274 | Row row; 275 | Cell cell;// 产生单元格 276 | CellStyle style = workbook.createCellStyle(); 277 | style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); 278 | style.setFillBackgroundColor(HSSFColor.GREY_40_PERCENT.index); 279 | 280 | // 判定表头是否需要需要增加额外的提示 281 | int startNo = 0; 282 | 283 | ExcelVO excelVo = clazz.getAnnotation(ExcelVO.class); 284 | String[] tips = excelVo.tips(); 285 | if (tips.length > 0) { 286 | row = sheet.createRow(startNo++); 287 | for (int i = 0; i < tips.length; i++) { 288 | cell = row.createCell(i);// 创建列 289 | cell.setCellType(HSSFCell.CELL_TYPE_STRING); 290 | cell.setCellValue(tips[i]);// 写入列名 291 | } 292 | } 293 | 294 | row = sheet.createRow(startNo++);// 产生一行 295 | // 写入各个字段的列头名称 296 | for (int i = 0; i < fields.size(); i++) { 297 | Field field = fields.get(i); 298 | ExcelVOAttribute attr = field.getAnnotation(ExcelVOAttribute.class); 299 | int col = getExcelCol(attr.column());// 获得列号 300 | cell = row.createCell(col);// 创建列 301 | cell.setCellType(HSSFCell.CELL_TYPE_STRING);// 设置列中写入内容为String类型 302 | cell.setCellValue(attr.name());// 写入列名 303 | 304 | // 如果设置了提示信息则鼠标放上去提示. 305 | // if (!attr.prompt().trim().equals("")) { 306 | // setHSSFPrompt(sheet, "", attr.prompt(), 1, 100, col, col);// 307 | // 这里默认设了2-101列提示. 308 | // } 309 | // // 如果设置了combo属性则本列只能选择不能输入 310 | // if (attr.combo().length > 0) { 311 | // setHSSFValidation(sheet, attr.combo(), 1, 100, col, col);// 312 | // 这里默认设了2-101列只能选择不能输入. 313 | // } 314 | cell.setCellStyle(style); 315 | } 316 | 317 | int endNo = list.size(); 318 | // 写入各条记录,每条记录对应excel表中的一行 319 | for (int i = 0; i < endNo; i++) { 320 | row = sheet.createRow(i + startNo); 321 | T vo = list.get(i); // 得到导出对象. 322 | for (int j = 0; j < fields.size(); j++) { 323 | Field field = fields.get(j);// 获得field. 324 | field.setAccessible(true);// 设置实体类私有属性可访问 325 | ExcelVOAttribute attr = field.getAnnotation(ExcelVOAttribute.class); 326 | try { 327 | // 根据ExcelVOAttribute中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. 328 | if (attr.isExport()) { 329 | cell = row.createCell(getExcelCol(attr.column()));// 创建cell 330 | cell.setCellType(HSSFCell.CELL_TYPE_STRING); 331 | Object o = field.get(vo); 332 | if (o == null) { 333 | cell.setCellValue(""); 334 | } else { 335 | if (field.getType() == Date.class) { 336 | cell.setCellValue(TimeUtil.format((Date) o, attr.dateFormat())); 337 | } else { 338 | cell.setCellValue(String.valueOf(o));// 如果数据存在就填入,不存在填入空格. 339 | } 340 | } 341 | } 342 | } catch (IllegalArgumentException e) { 343 | e.printStackTrace(); 344 | } catch (IllegalAccessException e) { 345 | e.printStackTrace(); 346 | } 347 | } 348 | } 349 | } 350 | 351 | try { 352 | output.flush(); 353 | workbook.write(output); 354 | return true; 355 | } catch (IOException e) { 356 | e.printStackTrace(); 357 | System.out.println("Output is closed "); 358 | return false; 359 | } finally { 360 | IOUtils.closeQuietly(workbook); 361 | IOUtils.closeQuietly(output); 362 | } 363 | 364 | } 365 | 366 | /** 367 | * 对list数据源将其里面的数据导入到excel表单 368 | * 369 | * @param sheetName 工作表的名称 370 | * @param sheetSize 每个sheet中数据的行数,此数值必须小于65536 371 | * @param output java输出流 372 | */ 373 | public boolean exportExcel(List list, String sheetName, OutputStream output) { 374 | List[] lists = new ArrayList[1]; 375 | lists[0] = list; 376 | 377 | String[] sheetNames = new String[1]; 378 | sheetNames[0] = sheetName; 379 | 380 | return exportExcel(lists, sheetNames, output); 381 | } 382 | 383 | /** 384 | * 将EXCEL中A,B,C,D,E列映射成0,1,2,3 385 | * 386 | * @param col 387 | */ 388 | public static int getExcelCol(String col) { 389 | col = col.toUpperCase(); 390 | // 从-1开始计算,字母重1开始运算。这种总数下来算数正好相同。 391 | int count = -1; 392 | char[] cs = col.toCharArray(); 393 | for (int i = 0; i < cs.length; i++) { 394 | count += (cs[i] - 64) * Math.pow(26, cs.length - 1 - i); 395 | } 396 | return count; 397 | } 398 | /** 399 | * 设置单元格上提示 400 | * 401 | * @param sheet 要设置的sheet. 402 | * @param promptTitle 标题 403 | * @param promptContent 内容 404 | * @param firstRow 开始行 405 | * @param endRow 结束行 406 | * @param firstCol 开始列 407 | * @param endCol 结束列 408 | * @return 设置好的sheet. 409 | */ 410 | // public static Sheet setHSSFPrompt(SXSSFSheet sheet, String promptTitle, 411 | // String promptContent, int firstRow, int endRow, int firstCol, 412 | // int endCol) { 413 | // // 构造constraint对象 414 | // DVConstraint constraint = DVConstraint 415 | // .createCustomFormulaConstraint("DD1"); 416 | // // 四个参数分别是:起始行、终止行、起始列、终止列 417 | // CellRangeAddressList regions = new CellRangeAddressList(firstRow, 418 | // endRow, firstCol, endCol); 419 | // // 数据有效性对象 420 | // HSSFDataValidation data_validation_list = new HSSFDataValidation( 421 | // regions, constraint); 422 | // sheet.addValidationData(data_validation_list); 423 | // 424 | // return sheet; 425 | // } 426 | 427 | /** 428 | * 设置某些列的值只能输入预制的数据,显示下拉框. 429 | * 430 | * @param sheet 要设置的sheet. 431 | * @param textlist 下拉框显示的内容 432 | * @param firstRow 开始行 433 | * @param endRow 结束行 434 | * @param firstCol 开始列 435 | * @param endCol 结束列 436 | * @return 设置好的sheet. 437 | */ 438 | // public static Sheet setHSSFValidation(Sheet sheet, 439 | // String[] textlist, int firstRow, int endRow, int firstCol, 440 | // int endCol) { 441 | // // 加载下拉列表内容 442 | // DVConstraint constraint = DVConstraint 443 | // .createExplicitListConstraint(textlist); 444 | // // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 445 | // CellRangeAddressList regions = new CellRangeAddressList(firstRow, 446 | // endRow, firstCol, endCol); 447 | // // 数据有效性对象 448 | // HSSFDataValidation data_validation_list = new HSSFDataValidation( 449 | // regions, constraint); 450 | // sheet.addValidationData(data_validation_list); 451 | // return sheet; 452 | // } 453 | 454 | /** 455 | * 得到实体类所有通过注解映射了数据表的字段 456 | * 457 | * @param map 458 | * @return 459 | */ 460 | public static List getMappedFiled(Class clazz, List fields) { 461 | if (fields == null) { 462 | fields = new ArrayList(); 463 | } 464 | 465 | Field[] allFields = clazz.getDeclaredFields();// 得到所有定义字段 466 | // 得到所有field并存放到一个list中. 467 | for (Field field : allFields) { 468 | if (field.isAnnotationPresent(ExcelVOAttribute.class)) { 469 | fields.add(field); 470 | } 471 | } 472 | if (clazz.getSuperclass() != null && !clazz.getSuperclass().equals(Object.class)) { 473 | getMappedFiled(clazz.getSuperclass(), fields); 474 | } 475 | 476 | return fields; 477 | } 478 | 479 | // 表头生成 480 | public static void createTableHeader(HSSFSheet sheet, String[] tableHeader, String sheetname) { 481 | HSSFRow row = null; 482 | HSSFHeader header = sheet.getHeader(); 483 | header.setCenter(sheetname); 484 | row = sheet.createRow(0); 485 | for (int i = 0; i < tableHeader.length; i++) { 486 | setCell(row, i, tableHeader[i]); 487 | } 488 | } 489 | 490 | // 写入单元格 491 | @SuppressWarnings("deprecation") 492 | public static void setCell(HSSFRow row, int index, String value) { 493 | HSSFCell cell = row.createCell((short) index); 494 | cell.setCellType(HSSFCell.CELL_TYPE_STRING); 495 | cell.setCellValue(value); 496 | } 497 | /** 498 | * 499 | * 解析excel表格头信息 500 | * @throws IOException 501 | */ 502 | public String[] paseExcelHead(String sheetName, InputStream input) throws Exception { 503 | Workbook workbook = create(input); 504 | Sheet sheet = workbook.getSheet(sheetName); 505 | if (!sheetName.trim().equals("")) { 506 | sheet = workbook.getSheet(sheetName);// 如果指定sheet名,则取指定sheet中的内容. 507 | } 508 | if (sheet == null) { 509 | sheet = workbook.getSheetAt(0); // 如果传入的sheet名不存在则默认指向第1个sheet. 510 | } 511 | Row row = sheet.getRow(0); 512 | int cells = row.getPhysicalNumberOfCells(); 513 | String[] excelHead = new String[cells]; 514 | for (int j = 0; j < cells; j++) { 515 | Cell cell = row.getCell(j); 516 | String c = parseExcel(cell, 0); 517 | excelHead[j] = c; 518 | } 519 | return excelHead; 520 | } 521 | 522 | 523 | public boolean viladHead(String sheetName, InputStream input,String[] defineHeads){ 524 | boolean b = true; 525 | try { 526 | String[] heads = this.paseExcelHead(sheetName,input); 527 | if(defineHeads.length>heads.length){ 528 | return false; 529 | } 530 | for(int i = 0;i= min && length <= max)) { 558 | b = false; 559 | } 560 | // 验证是否必填 561 | if (required && StringUtils.isEmpty(value)) { 562 | b = false; 563 | } 564 | if (!StringUtils.isEmpty(value)) { 565 | // 验证是否符合正则规则 566 | Pattern r = Pattern.compile(regx); 567 | Matcher m = r.matcher(value); 568 | if (!m.find()) { 569 | b = false; 570 | } 571 | } 572 | if (!b) { 573 | filed = message; 574 | } 575 | } 576 | return new ResJson(b, filed); 577 | } 578 | 579 | public static class ResJson { 580 | boolean isSuccess; 581 | String message; 582 | 583 | public ResJson(boolean isSuccess, String message) { 584 | this.message = message; 585 | this.isSuccess = isSuccess; 586 | } 587 | 588 | public boolean isSuccess() { 589 | return isSuccess; 590 | } 591 | 592 | public void setSuccess(boolean success) { 593 | isSuccess = success; 594 | } 595 | 596 | public String getMessage() { 597 | return message; 598 | } 599 | 600 | public void setMessage(String message) { 601 | this.message = message; 602 | } 603 | } 604 | 605 | } 606 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/excel/ExcelVO.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util.excel; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | 7 | @Retention(RetentionPolicy.RUNTIME) 8 | @Target( { java.lang.annotation.ElementType.TYPE }) 9 | public @interface ExcelVO { 10 | 11 | int skipLine() default 1; // 默认跳过表头 12 | String[] tips() default {}; //导出excel时 添加表头顶部的说明 13 | } 14 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/excel/ExcelVOAttribute.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util.excel; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | 7 | @Retention(RetentionPolicy.RUNTIME) 8 | @Target( { java.lang.annotation.ElementType.FIELD }) 9 | public @interface ExcelVOAttribute { 10 | 11 | /** 12 | * 导出到Excel中的名字. 13 | */ 14 | String name(); 15 | 16 | /** 17 | * 配置列的名称,对应A,B,C,D.... 18 | */ 19 | String column(); 20 | 21 | /** 22 | * 提示信息 23 | */ 24 | String prompt() default ""; 25 | 26 | /** 27 | * 设置只能选择不能输入的列内容. 28 | */ 29 | String[] combo() default {}; 30 | 31 | /** 32 | * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. 33 | */ 34 | boolean isExport() default true; 35 | 36 | /** 37 | * 日期导出格式 38 | */ 39 | String dateFormat() default "yyyy-MM-dd HH:mm:ss"; 40 | 41 | /** 42 | * 数值转String时所保留小树点数 43 | */ 44 | int decimalLength() default 0; 45 | } 46 | -------------------------------------------------------------------------------- /lccf-util/src/main/java/com/lccf/util/excel/ExcelValidAttribute.java: -------------------------------------------------------------------------------- 1 | package com.lccf.util.excel; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | 7 | @Retention(RetentionPolicy.RUNTIME) 8 | @Target( { java.lang.annotation.ElementType.FIELD }) 9 | public @interface ExcelValidAttribute { 10 | boolean required() default false; 11 | String regexp() default ""; 12 | String message() default ""; 13 | int min() default 0; 14 | int max() default Integer.MAX_VALUE; 15 | } 16 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.lccf 7 | lccf 8 | 0.0.1-SNAPSHOT 9 | pom 10 | 11 | 12 | 13 | lcc-web 14 | lccf-util 15 | 16 | lccf-base 17 | 18 | 19 | lccf 20 | lccf 基于springboot 搭建的框架 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-parent 25 | 1.5.1.RELEASE 26 | 27 | 28 | 29 | 30 | UTF-8 31 | UTF-8 32 | 1.8 33 | 1 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-logging 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-aop 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-data-jpa 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-data-redis 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-cache 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-starter-validation 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-starter-web 73 | 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-test 79 | test 80 | 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-devtools 85 | true 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-configuration-processor 90 | true 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-security 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-starter-thymeleaf 99 | 100 | 101 | org.springframework.boot 102 | spring-boot-starter-mail 103 | 104 | 105 | org.springframework 106 | springloaded 107 | 108 | 109 | io.jsonwebtoken 110 | jjwt 111 | 0.7.0 112 | 113 | 114 | mysql 115 | mysql-connector-java 116 | 117 | 118 | io.springfox 119 | springfox-swagger2 120 | 2.4.0 121 | 122 | 123 | io.springfox 124 | springfox-swagger-ui 125 | 2.4.0 126 | 127 | 128 | io.springfox 129 | springfox-staticdocs 130 | 2.4.0 131 | 132 | 133 | 134 | org.springframework.restdocs 135 | spring-restdocs-mockmvc 136 | 1.1.2.RELEASE 137 | 138 | 139 | net.sf.dozer 140 | dozer 141 | 5.4.0 142 | 143 | 144 | org.slf4j 145 | slf4j-log4j12 146 | 147 | 148 | 149 | 150 | commons-beanutils 151 | commons-beanutils 152 | 1.9.3 153 | 154 | 155 | org.apache.commons 156 | commons-lang3 157 | 3.6 158 | 159 | 160 | 161 | 162 | 163 | com.baidu.disconf 164 | disconf-client 165 | 2.6.36 166 | 167 | 168 | javax.inject 169 | javax.inject 170 | ${javax.inject.version} 171 | 172 | 173 | 174 | com.fasterxml.jackson.datatype 175 | jackson-datatype-hibernate4 176 | ${jackson.version} 177 | 178 | 179 | com.fasterxml.jackson.datatype 180 | jackson-datatype-hppc 181 | ${jackson.version} 182 | 183 | 184 | com.fasterxml.jackson.datatype 185 | jackson-datatype-json-org 186 | ${jackson.version} 187 | 188 | 189 | com.fasterxml.jackson.datatype 190 | jackson-datatype-jsr310 191 | 192 | 193 | com.fasterxml.jackson.core 194 | jackson-annotations 195 | ${jackson.version} 196 | 197 | 198 | com.fasterxml.jackson.core 199 | jackson-databind 200 | ${jackson.version} 201 | 202 | 203 | com.fasterxml.jackson.core 204 | jackson-core 205 | ${jackson.version} 206 | 207 | 208 | 209 | com.relops 210 | snowflake 211 | 1.1 212 | 213 | 214 | 215 | 216 | org.springframework.boot 217 | spring-boot-starter-log4j2 218 | 219 | 220 | 221 | 222 | com.fasterxml.jackson.dataformat 223 | jackson-dataformat-yaml 224 | 225 | 226 | 227 | 228 | 229 | org.projectlombok 230 | lombok 231 | 1.16.8 232 | 233 | 234 | 235 | 236 | 237 | com.graphql-java 238 | graphql-java 239 | 3.0.0 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | org.apache.maven.plugins 251 | maven-compiler-plugin 252 | 253 | ${java.version} 254 | ${java.version} 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | --------------------------------------------------------------------------------