21 changed files with 1312 additions and 12 deletions
			
			
		- 
					198common/src/main/java/com/storeroom/config/RedisConfig.java
- 
					109common/src/main/java/com/storeroom/config/SwaggerConfig.java
- 
					2common/src/main/java/com/storeroom/utils/RsaUtils.java
- 
					3system/src/main/java/com/storeroom/modules/security/controller/AuthorizationController.java
- 
					52system/src/main/java/com/storeroom/modules/security/controller/OnlineController.java
- 
					14system/src/main/java/com/storeroom/modules/system/domain/User.java
- 
					117system/src/main/resources/application-prod.yml
- 
					3system/src/main/resources/application.yml
- 
					BINsystem/src/main/resources/ip2region/ip2region.db
- 
					48system/src/main/resources/template/email/email.ftl
- 
					69system/src/main/resources/template/email/taskAlarm.ftl
- 
					73system/src/main/resources/template/generator/admin/Controller.ftl
- 
					40system/src/main/resources/template/generator/admin/Dto.ftl
- 
					71system/src/main/resources/template/generator/admin/Entity.ftl
- 
					18system/src/main/resources/template/generator/admin/Mapper.ftl
- 
					67system/src/main/resources/template/generator/admin/QueryCriteria.ftl
- 
					26system/src/main/resources/template/generator/admin/Repository.ftl
- 
					69system/src/main/resources/template/generator/admin/Service.ftl
- 
					143system/src/main/resources/template/generator/admin/ServiceImpl.ftl
- 
					27system/src/main/resources/template/generator/front/api.ftl
- 
					175system/src/main/resources/template/generator/front/index.ftl
| @ -0,0 +1,198 @@ | |||||
|  | package com.storeroom.config; | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | import cn.hutool.core.lang.Assert; | ||||
|  | import com.alibaba.fastjson.JSON; | ||||
|  | import com.alibaba.fastjson.parser.ParserConfig; | ||||
|  | import com.alibaba.fastjson.serializer.SerializerFeature; | ||||
|  | import com.storeroom.utils.StringUtils; | ||||
|  | import lombok.extern.slf4j.Slf4j; | ||||
|  | import org.apache.commons.codec.digest.DigestUtils; | ||||
|  | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||||
|  | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||||
|  | import org.springframework.boot.autoconfigure.data.redis.RedisProperties; | ||||
|  | import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
|  | import org.springframework.cache.Cache; | ||||
|  | import org.springframework.cache.annotation.CachingConfigurerSupport; | ||||
|  | import org.springframework.cache.annotation.EnableCaching; | ||||
|  | import org.springframework.cache.interceptor.CacheErrorHandler; | ||||
|  | import org.springframework.cache.interceptor.KeyGenerator; | ||||
|  | import org.springframework.context.annotation.Bean; | ||||
|  | import org.springframework.context.annotation.Configuration; | ||||
|  | import org.springframework.data.redis.cache.RedisCacheConfiguration; | ||||
|  | import org.springframework.data.redis.connection.RedisConnectionFactory; | ||||
|  | import org.springframework.data.redis.core.RedisOperations; | ||||
|  | import org.springframework.data.redis.core.RedisTemplate; | ||||
|  | import org.springframework.data.redis.serializer.RedisSerializationContext; | ||||
|  | import org.springframework.data.redis.serializer.RedisSerializer; | ||||
|  | 
 | ||||
|  | import java.nio.charset.Charset; | ||||
|  | import java.nio.charset.StandardCharsets; | ||||
|  | import java.time.Duration; | ||||
|  | import java.util.HashMap; | ||||
|  | import java.util.Map; | ||||
|  | 
 | ||||
|  | @Slf4j | ||||
|  | @Configuration | ||||
|  | @EnableCaching | ||||
|  | @ConditionalOnClass(RedisOperations.class) | ||||
|  | @EnableConfigurationProperties(RedisProperties.class) | ||||
|  | public class RedisConfig extends CachingConfigurerSupport { | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |      *  设置 redis 数据默认过期时间,默认2小时 | ||||
|  |      *  设置@cacheable 序列化方式 | ||||
|  |      */ | ||||
|  |     @Bean | ||||
|  |     public RedisCacheConfiguration redisCacheConfiguration(){ | ||||
|  |         FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); | ||||
|  |         RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig(); | ||||
|  |         configuration = configuration.serializeValuesWith(RedisSerializationContext. | ||||
|  |                 SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(6)); | ||||
|  |         return configuration; | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @SuppressWarnings("all") | ||||
|  |     @Bean(name = "redisTemplate") | ||||
|  |     @ConditionalOnMissingBean(name = "redisTemplate") | ||||
|  |     public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { | ||||
|  |         RedisTemplate<Object, Object> template = new RedisTemplate<>(); | ||||
|  |         //序列化 | ||||
|  |         FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); | ||||
|  |         // value值的序列化采用fastJsonRedisSerializer | ||||
|  |         template.setValueSerializer(fastJsonRedisSerializer); | ||||
|  |         template.setHashValueSerializer(fastJsonRedisSerializer); | ||||
|  |         // 全局开启AutoType,这里方便开发,使用全局的方式 | ||||
|  |         ParserConfig.getGlobalInstance().setAutoTypeSupport(true); | ||||
|  |         // 建议使用这种方式,小范围指定白名单 | ||||
|  |         // ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain"); | ||||
|  |         // key的序列化采用StringRedisSerializer | ||||
|  |         template.setKeySerializer(new StringRedisSerializer()); | ||||
|  |         template.setHashKeySerializer(new StringRedisSerializer()); | ||||
|  |         template.setConnectionFactory(redisConnectionFactory); | ||||
|  |         return template; | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |      * 自定义缓存key生成策略,默认将使用该策略 | ||||
|  |      */ | ||||
|  |     @Bean | ||||
|  |     @Override | ||||
|  |     public KeyGenerator keyGenerator() { | ||||
|  |         return (target, method, params) -> { | ||||
|  |             Map<String,Object> container = new HashMap<>(3); | ||||
|  |             Class<?> targetClassClass = target.getClass(); | ||||
|  |             // 类地址 | ||||
|  |             container.put("class",targetClassClass.toGenericString()); | ||||
|  |             // 方法名称 | ||||
|  |             container.put("methodName",method.getName()); | ||||
|  |             // 包名称 | ||||
|  |             container.put("package",targetClassClass.getPackage()); | ||||
|  |             // 参数列表 | ||||
|  |             for (int i = 0; i < params.length; i++) { | ||||
|  |                 container.put(String.valueOf(i),params[i]); | ||||
|  |             } | ||||
|  |             // 转为JSON字符串 | ||||
|  |             String jsonString = JSON.toJSONString(container); | ||||
|  |             // 做SHA256 Hash计算,得到一个SHA256摘要作为Key | ||||
|  |             return DigestUtils.sha256Hex(jsonString); | ||||
|  |         }; | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Bean | ||||
|  |     @Override | ||||
|  |     public CacheErrorHandler errorHandler() { | ||||
|  |         // 异常处理,当Redis发生异常时,打印日志,但是程序正常走 | ||||
|  |         log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); | ||||
|  |         return new CacheErrorHandler() { | ||||
|  |             @Override | ||||
|  |             public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { | ||||
|  |                 log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); | ||||
|  |             } | ||||
|  | 
 | ||||
|  |             @Override | ||||
|  |             public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { | ||||
|  |                 log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); | ||||
|  |             } | ||||
|  | 
 | ||||
|  |             @Override | ||||
|  |             public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { | ||||
|  |                 log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); | ||||
|  |             } | ||||
|  | 
 | ||||
|  |             @Override | ||||
|  |             public void handleCacheClearError(RuntimeException e, Cache cache) { | ||||
|  |                 log.error("Redis occur handleCacheClearError:", e); | ||||
|  |             } | ||||
|  |         }; | ||||
|  |     } | ||||
|  | 
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | /** | ||||
|  |  * Value 序列化 | ||||
|  |  * | ||||
|  |  * @author / | ||||
|  |  * @param <T> | ||||
|  |  */ | ||||
|  | class FastJsonRedisSerializer<T> implements RedisSerializer<T> { | ||||
|  | 
 | ||||
|  |     private final Class<T> clazz; | ||||
|  | 
 | ||||
|  |     FastJsonRedisSerializer(Class<T> clazz) { | ||||
|  |         super(); | ||||
|  |         this.clazz = clazz; | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public byte[] serialize(T t) { | ||||
|  |         if (t == null) { | ||||
|  |             return new byte[0]; | ||||
|  |         } | ||||
|  |         return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public T deserialize(byte[] bytes) { | ||||
|  |         if (bytes == null || bytes.length <= 0) { | ||||
|  |             return null; | ||||
|  |         } | ||||
|  |         String str = new String(bytes, StandardCharsets.UTF_8); | ||||
|  |         return JSON.parseObject(str, clazz); | ||||
|  |     } | ||||
|  | 
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | /** | ||||
|  |  * 重写序列化器 | ||||
|  |  * | ||||
|  |  * @author / | ||||
|  |  */ | ||||
|  | class StringRedisSerializer implements RedisSerializer<Object> { | ||||
|  | 
 | ||||
|  |     private final Charset charset; | ||||
|  | 
 | ||||
|  |     StringRedisSerializer() { | ||||
|  |         this(StandardCharsets.UTF_8); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     private StringRedisSerializer(Charset charset) { | ||||
|  |         Assert.notNull(charset, "Charset must not be null!"); | ||||
|  |         this.charset = charset; | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public String deserialize(byte[] bytes) { | ||||
|  |         return (bytes == null ? null : new String(bytes, charset)); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public byte[] serialize(Object object) { | ||||
|  |         String string = JSON.toJSONString(object); | ||||
|  |         if (StringUtils.isBlank(string)) { | ||||
|  |             return null; | ||||
|  |         } | ||||
|  |         string = string.replace("\"", ""); | ||||
|  |         return string.getBytes(charset); | ||||
|  |     } | ||||
|  | } | ||||
| @ -0,0 +1,109 @@ | |||||
|  | package com.storeroom.config; | ||||
|  | 
 | ||||
|  | import com.fasterxml.classmate.TypeResolver; | ||||
|  | import com.google.common.base.Predicates; | ||||
|  | import io.swagger.annotations.ApiModel; | ||||
|  | import io.swagger.annotations.ApiModelProperty; | ||||
|  | import lombok.Data; | ||||
|  | import org.springframework.beans.factory.annotation.Value; | ||||
|  | import org.springframework.context.annotation.Bean; | ||||
|  | import org.springframework.context.annotation.Configuration; | ||||
|  | import org.springframework.core.Ordered; | ||||
|  | import org.springframework.data.domain.Pageable; | ||||
|  | import springfox.documentation.builders.ApiInfoBuilder; | ||||
|  | import springfox.documentation.builders.ParameterBuilder; | ||||
|  | import springfox.documentation.builders.PathSelectors; | ||||
|  | import springfox.documentation.schema.AlternateTypeRule; | ||||
|  | import springfox.documentation.schema.AlternateTypeRuleConvention; | ||||
|  | import springfox.documentation.schema.ModelRef; | ||||
|  | import springfox.documentation.service.ApiInfo; | ||||
|  | import springfox.documentation.service.Parameter; | ||||
|  | import springfox.documentation.spi.DocumentationType; | ||||
|  | import springfox.documentation.spring.web.plugins.Docket; | ||||
|  | import springfox.documentation.swagger2.annotations.EnableSwagger2; | ||||
|  | 
 | ||||
|  | import java.util.ArrayList; | ||||
|  | import java.util.List; | ||||
|  | 
 | ||||
|  | import static com.google.common.collect.Lists.newArrayList; | ||||
|  | import static springfox.documentation.schema.AlternateTypeRules.newRule; | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | @Configuration | ||||
|  | @EnableSwagger2 | ||||
|  | public class SwaggerConfig { | ||||
|  | 
 | ||||
|  |     @Value("${jwt.header}") | ||||
|  |     private String tokenHeader; | ||||
|  | 
 | ||||
|  |     @Value("${jwt.token-start-with}") | ||||
|  |     private String tokenStartWith; | ||||
|  | 
 | ||||
|  |     @Value("${swagger.enabled}") | ||||
|  |     private Boolean enabled; | ||||
|  | 
 | ||||
|  |     @Bean | ||||
|  |     @SuppressWarnings("all") | ||||
|  |     public Docket createRestApi() { | ||||
|  |         ParameterBuilder ticketPar = new ParameterBuilder(); | ||||
|  |         List<Parameter> pars = new ArrayList<>(); | ||||
|  |         ticketPar.name(tokenHeader).description("token") | ||||
|  |                 .modelRef(new ModelRef("string")) | ||||
|  |                 .parameterType("header") | ||||
|  |                 .defaultValue(tokenStartWith + " ") | ||||
|  |                 .required(true) | ||||
|  |                 .build(); | ||||
|  |         pars.add(ticketPar.build()); | ||||
|  |         return new Docket(DocumentationType.SWAGGER_2) | ||||
|  |                 .enable(enabled) | ||||
|  |                 .apiInfo(apiInfo()) | ||||
|  |                 .select() | ||||
|  |                 .paths(Predicates.not(PathSelectors.regex("/error.*"))) | ||||
|  |                 .build() | ||||
|  |                 .globalOperationParameters(pars); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     private ApiInfo apiInfo() { | ||||
|  |         return new ApiInfoBuilder() | ||||
|  |                 .description("阅行客后台管理系统") | ||||
|  |                 .title("YXK-ADMIN 接口文档") | ||||
|  |                 .version("2.4") | ||||
|  |                 .build(); | ||||
|  |     } | ||||
|  | 
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | /** | ||||
|  |  *  将Pageable转换展示在swagger中 | ||||
|  |  */ | ||||
|  | @Configuration | ||||
|  | class SwaggerDataConfig { | ||||
|  | 
 | ||||
|  |     @Bean | ||||
|  |     public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) { | ||||
|  |         return new AlternateTypeRuleConvention() { | ||||
|  |             @Override | ||||
|  |             public int getOrder() { | ||||
|  |                 return Ordered.HIGHEST_PRECEDENCE; | ||||
|  |             } | ||||
|  | 
 | ||||
|  |             @Override | ||||
|  |             public List<AlternateTypeRule> rules() { | ||||
|  |                 return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class))); | ||||
|  |             } | ||||
|  |         }; | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @ApiModel | ||||
|  |     @Data | ||||
|  |     private static class Page { | ||||
|  |         @ApiModelProperty("页码 (0..N)") | ||||
|  |         private Integer page; | ||||
|  | 
 | ||||
|  |         @ApiModelProperty("每页显示的数目") | ||||
|  |         private Integer size; | ||||
|  | 
 | ||||
|  |         @ApiModelProperty("以下列格式排序标准:property[,asc | desc]。 默认排序顺序为升序。 支持多种排序条件:如:id,asc") | ||||
|  |         private List<String> sort; | ||||
|  |     } | ||||
|  | } | ||||
| @ -0,0 +1,52 @@ | |||||
|  | package com.storeroom.modules.security.controller; | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | import com.storeroom.modules.security.service.OnlineUserService; | ||||
|  | import com.storeroom.utils.EncryptUtils; | ||||
|  | import io.swagger.annotations.Api; | ||||
|  | import io.swagger.annotations.ApiOperation; | ||||
|  | import lombok.RequiredArgsConstructor; | ||||
|  | import org.springframework.data.domain.Pageable; | ||||
|  | import org.springframework.http.HttpStatus; | ||||
|  | import org.springframework.http.ResponseEntity; | ||||
|  | import org.springframework.security.access.prepost.PreAuthorize; | ||||
|  | import org.springframework.web.bind.annotation.*; | ||||
|  | 
 | ||||
|  | import javax.servlet.http.HttpServletResponse; | ||||
|  | import java.io.IOException; | ||||
|  | import java.util.Set; | ||||
|  | 
 | ||||
|  | @RestController | ||||
|  | @RequiredArgsConstructor | ||||
|  | @RequestMapping("/auth/online") | ||||
|  | @Api(tags = "系统:在线用户管理") | ||||
|  | public class OnlineController { | ||||
|  | 
 | ||||
|  |     private final OnlineUserService onlineUserService; | ||||
|  | 
 | ||||
|  |     @ApiOperation("查询在线用户") | ||||
|  |     @GetMapping | ||||
|  |     //@PreAuthorize("@el.check()") | ||||
|  |     public ResponseEntity<Object> query(String filter, Pageable pageable){ | ||||
|  |         return new ResponseEntity<>(onlineUserService.getAll(filter, pageable), HttpStatus.OK); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @ApiOperation("导出数据") | ||||
|  |     @GetMapping(value = "/download") | ||||
|  |     //@PreAuthorize("@el.check()") | ||||
|  |     public void download(HttpServletResponse response, String filter) throws IOException { | ||||
|  |         onlineUserService.download(onlineUserService.getAll(filter), response); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @ApiOperation("踢出用户") | ||||
|  |     @DeleteMapping | ||||
|  |     //@PreAuthorize("@el.check()") | ||||
|  |     public ResponseEntity<Object> delete(@RequestBody Set<String> keys) throws Exception { | ||||
|  |         for (String key : keys) { | ||||
|  |             // 解密Key | ||||
|  |             key = EncryptUtils.desDecrypt(key); | ||||
|  |             onlineUserService.kickOut(key); | ||||
|  |         } | ||||
|  |         return new ResponseEntity<>(HttpStatus.OK); | ||||
|  |     } | ||||
|  | } | ||||
| @ -0,0 +1,117 @@ | |||||
|  | #配置数据源 | ||||
|  | spring: | ||||
|  |   datasource: | ||||
|  |     druid: | ||||
|  |       db-type: com.alibaba.druid.pool.DruidDataSource | ||||
|  |       driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy | ||||
|  |       url: jdbc:log4jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:yxkadmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false | ||||
|  |       username: ${DB_USER:root} | ||||
|  |       password: ${DB_PWD:ftzn83560792} | ||||
|  |       # 初始连接数 | ||||
|  |       initial-size: 5 | ||||
|  |       # 最小连接数 | ||||
|  |       min-idle: 10 | ||||
|  |       # 最大连接数 | ||||
|  |       max-active: 20 | ||||
|  |       # 获取连接超时时间 | ||||
|  |       max-wait: 5000 | ||||
|  |       # 连接有效性检测时间 | ||||
|  |       time-between-eviction-runs-millis: 60000 | ||||
|  |       # 连接在池中最小生存的时间 | ||||
|  |       min-evictable-idle-time-millis: 300000 | ||||
|  |       # 连接在池中最大生存的时间 | ||||
|  |       max-evictable-idle-time-millis: 900000 | ||||
|  |       test-while-idle: true | ||||
|  |       test-on-borrow: false | ||||
|  |       test-on-return: false | ||||
|  |       # 检测连接是否有效 | ||||
|  |       validation-query: select 1 | ||||
|  |       # 配置监控统计 | ||||
|  |       webStatFilter: | ||||
|  |         enabled: true | ||||
|  |       stat-view-servlet: | ||||
|  |         enabled: true | ||||
|  |         url-pattern: /druid/* | ||||
|  |         reset-enable: false | ||||
|  |         login-username: admin | ||||
|  |         login-password: 123456 | ||||
|  |       filter: | ||||
|  |         stat: | ||||
|  |           enabled: true | ||||
|  |           # 记录慢SQL | ||||
|  |           log-slow-sql: true | ||||
|  |           slow-sql-millis: 1000 | ||||
|  |           merge-sql: true | ||||
|  |         wall: | ||||
|  |           config: | ||||
|  |             multi-statement-allow: true | ||||
|  | 
 | ||||
|  | # 登录相关配置 | ||||
|  | login: | ||||
|  |   # 登录缓存 | ||||
|  |   cache-enable: true | ||||
|  |   #  是否限制单用户登录 | ||||
|  |   single-login: false | ||||
|  |   #  验证码 | ||||
|  |   login-code: | ||||
|  |     #  验证码类型配置 查看 LoginProperties 类 | ||||
|  |     code-type: arithmetic | ||||
|  |     #  登录图形验证码有效时间/分钟 | ||||
|  |     expiration: 2 | ||||
|  |     #  验证码高度 | ||||
|  |     width: 111 | ||||
|  |     #  验证码宽度 | ||||
|  |     heigth: 36 | ||||
|  |     # 内容长度 | ||||
|  |     length: 2 | ||||
|  |     # 字体名称,为空则使用默认字体,如遇到线上乱码,设置其他字体即可 | ||||
|  |     font-name: | ||||
|  |     # 字体大小 | ||||
|  |     font-size: 25 | ||||
|  | 
 | ||||
|  | #jwt | ||||
|  | jwt: | ||||
|  |   header: Authorization | ||||
|  |   # 令牌前缀 | ||||
|  |   token-start-with: Bearer | ||||
|  |   # 必须使用最少88位的Base64对该令牌进行编码 | ||||
|  |   base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= | ||||
|  |   # 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html | ||||
|  |   token-validity-in-seconds: 7200000 | ||||
|  |   # 在线用户key | ||||
|  |   online-key: online-token- | ||||
|  |   # 验证码 | ||||
|  |   code-key: code-key- | ||||
|  |   # token 续期检查时间范围(默认30分钟,单位默认毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期 | ||||
|  |   detect: 1800000 | ||||
|  |   # 续期时间范围,默认 1小时,这里单位毫秒 | ||||
|  |   renew: 3600000 | ||||
|  | 
 | ||||
|  | # IP 本地解析 | ||||
|  | ip: | ||||
|  |   local-parsing: false | ||||
|  | 
 | ||||
|  | #是否允许生成代码,生产环境设置为false | ||||
|  | generator: | ||||
|  |   enabled: false | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | #是否开启 swagger-ui | ||||
|  | swagger: | ||||
|  |   enabled: true | ||||
|  | 
 | ||||
|  | # 文件存储路径 | ||||
|  | file: | ||||
|  |   mac: | ||||
|  |     path: ~/file/ | ||||
|  |     avatar: ~/avatar/ | ||||
|  |   linux: | ||||
|  |     path: /home/www/yxkadmin/file/ | ||||
|  |     avatar: /home/www/yxkadmin/avatar/ | ||||
|  |   windows: | ||||
|  |     path: D:\yxk-storetoom\file\ | ||||
|  |     avatar: D:\yxk-storetoom\avatar\ | ||||
|  |   # 文件大小 /M | ||||
|  |   maxSize: 100 | ||||
|  |   avatarMaxSize: 5 | ||||
| @ -0,0 +1,48 @@ | |||||
|  | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||||
|  | <html xmlns="http://www.w3.org/1999/xhtml"> | ||||
|  | <head> | ||||
|  |     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | ||||
|  |     <style> | ||||
|  |         @page { | ||||
|  |             margin: 0; | ||||
|  |         } | ||||
|  |     </style> | ||||
|  | </head> | ||||
|  | <body style="margin: 0px; | ||||
|  |             padding: 0px; | ||||
|  | 			font: 100% SimSun, Microsoft YaHei, Times New Roman, Verdana, Arial, Helvetica, sans-serif; | ||||
|  |             color: #000;"> | ||||
|  | <div style="height: auto; | ||||
|  | 			width: 820px; | ||||
|  | 			min-width: 820px; | ||||
|  | 			margin: 0 auto; | ||||
|  | 			margin-top: 20px; | ||||
|  |             border: 1px solid #eee;"> | ||||
|  |     <div style="padding: 10px;padding-bottom: 0px;"> | ||||
|  |         <p style="margin-bottom: 10px;padding-bottom: 0px;">尊敬的用户,您好:</p> | ||||
|  |         <p style="text-indent: 2em; margin-bottom: 10px;">您正在申请邮箱验证,您的验证码为:</p> | ||||
|  |         <p style="text-align: center; | ||||
|  | 			font-family: Times New Roman; | ||||
|  | 			font-size: 22px; | ||||
|  | 			color: #C60024; | ||||
|  | 			padding: 20px 0px; | ||||
|  | 			margin-bottom: 10px; | ||||
|  | 			font-weight: bold; | ||||
|  | 			background: #ebebeb;">${code}</p> | ||||
|  |         <div class="foot-hr hr" style="margin: 0 auto; | ||||
|  | 			z-index: 111; | ||||
|  | 			width: 800px; | ||||
|  | 			margin-top: 30px; | ||||
|  | 			border-top: 1px solid #DA251D;"> | ||||
|  |         </div> | ||||
|  |         <div style="text-align: center; | ||||
|  | 			font-size: 12px; | ||||
|  | 			padding: 20px 0px; | ||||
|  | 			font-family: Microsoft YaHei;"> | ||||
|  |             Copyright ©${.now?string("yyyy")} <a hover="color: #DA251D;" style="color: #999;" href="" target="_blank">yxk-ADMIN</a> 阅行客后台管理系统. | ||||
|  |         </div> | ||||
|  | 
 | ||||
|  |     </div> | ||||
|  | </div> | ||||
|  | </body> | ||||
|  | </html> | ||||
| @ -0,0 +1,69 @@ | |||||
|  | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||||
|  | <html xmlns="http://www.w3.org/1999/xhtml"> | ||||
|  | <head> | ||||
|  |     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | ||||
|  |     <style> | ||||
|  |         @page { | ||||
|  |             margin: 0; | ||||
|  |         } | ||||
|  |     </style> | ||||
|  | </head> | ||||
|  | <body style="margin: 0px; | ||||
|  |             padding: 0px; | ||||
|  | 			font: 100% SimSun, Microsoft YaHei, Times New Roman, Verdana, Arial, Helvetica, sans-serif; | ||||
|  |             color: #000;"> | ||||
|  | <div style="height: auto; | ||||
|  | 			margin: 0 auto; | ||||
|  | 			margin-top: 20px; | ||||
|  | 			padding: 20px; | ||||
|  |             border: 1px solid #eee;"> | ||||
|  |         <div> | ||||
|  |             <p style="margin-bottom: 10px;">任务信息:</p> | ||||
|  |             <table style="border-collapse: collapse;"> | ||||
|  |                 <tr> | ||||
|  |                     <th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">任务名称</th> | ||||
|  |                     <th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">Bean名称</th> | ||||
|  |                     <th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">执行方法</th> | ||||
|  |                     <th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">参数内容</th> | ||||
|  |                     <th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">Cron表达式</th> | ||||
|  |                     <th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">描述内容</th> | ||||
|  |                 </tr> | ||||
|  |                 <tr> | ||||
|  |                     <td style="padding: .65em;border: 1px solid#777;">${task.jobName}</td> | ||||
|  |                     <td style="padding: .65em;border: 1px solid#777;">${task.beanName}</td> | ||||
|  |                     <td style="padding: .65em;border: 1px solid#777;">${task.methodName}</td> | ||||
|  |                     <td style="padding: .65em;border: 1px solid#777;">${(task.params)!""}</td> | ||||
|  |                     <td style="padding: .65em;border: 1px solid#777;">${task.cronExpression}</td> | ||||
|  |                     <td style="padding: .65em;border: 1px solid#777;">${(task.description)!""}</td> | ||||
|  |                 </tr> | ||||
|  |             </table> | ||||
|  |         </div> | ||||
|  |         <div> | ||||
|  |             <p style="margin-bottom: 10px;">异常信息:</p> | ||||
|  |             <pre style="position: relative; | ||||
|  |   padding: 15px; | ||||
|  |   line-height: 20px; | ||||
|  |   border-left: 5px solid #ddd; | ||||
|  |   color: #333; | ||||
|  |   font-family: Courier New, serif; | ||||
|  |   font-size: 12px"> | ||||
|  |                 ${msg} | ||||
|  |             </pre> | ||||
|  |         </div> | ||||
|  |         <div class="foot-hr hr" style="margin: 0 auto; | ||||
|  | 			z-index: 111; | ||||
|  | 			width: 800px; | ||||
|  | 			margin-top: 30px; | ||||
|  | 			border-top: 1px solid #DA251D;"> | ||||
|  |         </div> | ||||
|  |         <div style="text-align: center; | ||||
|  | 			font-size: 12px; | ||||
|  | 			padding: 20px 0px; | ||||
|  | 			font-family: Microsoft YaHei;"> | ||||
|  |             Copyright ©${.now?string("yyyy")} <a hover="color: #DA251D;" style="color: #999;" href="#" target="_blank">YXK-ADMIN</a> 阅行客后台管理系统. | ||||
|  |         </div> | ||||
|  | 
 | ||||
|  |     </div> | ||||
|  | </div> | ||||
|  | </body> | ||||
|  | </html> | ||||
| @ -0,0 +1,73 @@ | |||||
|  | 
 | ||||
|  | package ${package}.rest; | ||||
|  | 
 | ||||
|  | import com.yxkadmin.annotation.Log; | ||||
|  | import ${package}.domain.${className}; | ||||
|  | import ${package}.service.${className}Service; | ||||
|  | import ${package}.service.dto.${className}QueryCriteria; | ||||
|  | import org.springframework.data.domain.Pageable; | ||||
|  | import lombok.RequiredArgsConstructor; | ||||
|  | import org.springframework.http.HttpStatus; | ||||
|  | import org.springframework.http.ResponseEntity; | ||||
|  | import org.springframework.security.access.prepost.PreAuthorize; | ||||
|  | import org.springframework.validation.annotation.Validated; | ||||
|  | import org.springframework.web.bind.annotation.*; | ||||
|  | import io.swagger.annotations.*; | ||||
|  | import java.io.IOException; | ||||
|  | import javax.servlet.http.HttpServletResponse; | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://yxk-admin.vip | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | @RestController | ||||
|  | @RequiredArgsConstructor | ||||
|  | @Api(tags = "${apiAlias}管理") | ||||
|  | @RequestMapping("/api/${changeClassName}") | ||||
|  | public class ${className}Controller { | ||||
|  | 
 | ||||
|  |     private final ${className}Service ${changeClassName}Service; | ||||
|  | 
 | ||||
|  |     @Log("导出数据") | ||||
|  |     @ApiOperation("导出数据") | ||||
|  |     @GetMapping(value = "/download") | ||||
|  |     @PreAuthorize("@el.check('${changeClassName}:list')") | ||||
|  |     public void download(HttpServletResponse response, ${className}QueryCriteria criteria) throws IOException { | ||||
|  |         ${changeClassName}Service.download(${changeClassName}Service.queryAll(criteria), response); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @GetMapping | ||||
|  |     @Log("查询${apiAlias}") | ||||
|  |     @ApiOperation("查询${apiAlias}") | ||||
|  |     @PreAuthorize("@el.check('${changeClassName}:list')") | ||||
|  |     public ResponseEntity<Object> query(${className}QueryCriteria criteria, Pageable pageable){ | ||||
|  |         return new ResponseEntity<>(${changeClassName}Service.queryAll(criteria,pageable),HttpStatus.OK); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @PostMapping | ||||
|  |     @Log("新增${apiAlias}") | ||||
|  |     @ApiOperation("新增${apiAlias}") | ||||
|  |     @PreAuthorize("@el.check('${changeClassName}:add')") | ||||
|  |     public ResponseEntity<Object> create(@Validated @RequestBody ${className} resources){ | ||||
|  |         return new ResponseEntity<>(${changeClassName}Service.create(resources),HttpStatus.CREATED); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @PutMapping | ||||
|  |     @Log("修改${apiAlias}") | ||||
|  |     @ApiOperation("修改${apiAlias}") | ||||
|  |     @PreAuthorize("@el.check('${changeClassName}:edit')") | ||||
|  |     public ResponseEntity<Object> update(@Validated @RequestBody ${className} resources){ | ||||
|  |         ${changeClassName}Service.update(resources); | ||||
|  |         return new ResponseEntity<>(HttpStatus.NO_CONTENT); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Log("删除${apiAlias}") | ||||
|  |     @ApiOperation("删除${apiAlias}") | ||||
|  |     @PreAuthorize("@el.check('${changeClassName}:del')") | ||||
|  |     @DeleteMapping | ||||
|  |     public ResponseEntity<Object> delete(@RequestBody ${pkColumnType}[] ids) { | ||||
|  |         ${changeClassName}Service.deleteAll(ids); | ||||
|  |         return new ResponseEntity<>(HttpStatus.OK); | ||||
|  |     } | ||||
|  | } | ||||
| @ -0,0 +1,40 @@ | |||||
|  | 
 | ||||
|  | package ${package}.service.dto; | ||||
|  | 
 | ||||
|  | import lombok.Data; | ||||
|  | <#if hasTimestamp> | ||||
|  | import java.sql.Timestamp; | ||||
|  | </#if> | ||||
|  | <#if hasBigDecimal> | ||||
|  | import java.math.BigDecimal; | ||||
|  | </#if> | ||||
|  | import java.io.Serializable; | ||||
|  | <#if !auto && pkColumnType = 'Long'> | ||||
|  | import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||||
|  | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; | ||||
|  | </#if> | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://yxk-admin.vip | ||||
|  | * @description / | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | @Data | ||||
|  | public class ${className}Dto implements Serializable { | ||||
|  | <#if columns??> | ||||
|  |     <#list columns as column> | ||||
|  | 
 | ||||
|  |     <#if column.remark != ''> | ||||
|  |     /** ${column.remark} */ | ||||
|  |     </#if> | ||||
|  |     <#if column.columnKey = 'PRI'> | ||||
|  |     <#if !auto && pkColumnType = 'Long'> | ||||
|  |     /** 防止精度丢失 */ | ||||
|  |     @JsonSerialize(using= ToStringSerializer.class) | ||||
|  |     </#if> | ||||
|  |     </#if> | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  | } | ||||
| @ -0,0 +1,71 @@ | |||||
|  | 
 | ||||
|  | package ${package}.domain; | ||||
|  | 
 | ||||
|  | import lombok.Data; | ||||
|  | import cn.hutool.core.bean.BeanUtil; | ||||
|  | import io.swagger.annotations.ApiModelProperty; | ||||
|  | import cn.hutool.core.bean.copier.CopyOptions; | ||||
|  | import javax.persistence.*; | ||||
|  | <#if isNotNullColumns??> | ||||
|  | import javax.validation.constraints.*; | ||||
|  | </#if> | ||||
|  | <#if hasDateAnnotation> | ||||
|  | import javax.persistence.Entity; | ||||
|  | import javax.persistence.Table; | ||||
|  | import org.hibernate.annotations.*; | ||||
|  | </#if> | ||||
|  | <#if hasTimestamp> | ||||
|  | import java.sql.Timestamp; | ||||
|  | </#if> | ||||
|  | <#if hasBigDecimal> | ||||
|  | import java.math.BigDecimal; | ||||
|  | </#if> | ||||
|  | import java.io.Serializable; | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://yxk-admin | ||||
|  | * @description / | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | @Entity | ||||
|  | @Data | ||||
|  | @Table(name="${tableName}") | ||||
|  | public class ${className} implements Serializable { | ||||
|  | <#if columns??> | ||||
|  |     <#list columns as column> | ||||
|  | 
 | ||||
|  |     <#if column.columnKey = 'PRI'> | ||||
|  |     @Id | ||||
|  |     <#if auto> | ||||
|  |     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|  |     </#if> | ||||
|  |     </#if> | ||||
|  |     @Column(name = "${column.columnName}"<#if column.columnKey = 'UNI'>,unique = true</#if><#if column.istNotNull && column.columnKey != 'PRI'>,nullable = false</#if>) | ||||
|  |     <#if column.istNotNull && column.columnKey != 'PRI'> | ||||
|  |         <#if column.columnType = 'String'> | ||||
|  |     @NotBlank | ||||
|  |         <#else> | ||||
|  |     @NotNull | ||||
|  |         </#if> | ||||
|  |     </#if> | ||||
|  |     <#if (column.dateAnnotation)??> | ||||
|  |     <#if column.dateAnnotation = 'CreationTimestamp'> | ||||
|  |     @CreationTimestamp | ||||
|  |     <#else> | ||||
|  |     @UpdateTimestamp | ||||
|  |     </#if> | ||||
|  |     </#if> | ||||
|  |     <#if column.remark != ''> | ||||
|  |     @ApiModelProperty(value = "${column.remark}") | ||||
|  |     <#else> | ||||
|  |     @ApiModelProperty(value = "${column.changeColumnName}") | ||||
|  |     </#if> | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  | 
 | ||||
|  |     public void copy(${className} source){ | ||||
|  |         BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); | ||||
|  |     } | ||||
|  | } | ||||
| @ -0,0 +1,18 @@ | |||||
|  | 
 | ||||
|  | package ${package}.service.mapstruct; | ||||
|  | 
 | ||||
|  | import com.yxkadmin.base.BaseMapper; | ||||
|  | import ${package}.domain.${className}; | ||||
|  | import ${package}.service.dto.${className}Dto; | ||||
|  | import org.mapstruct.Mapper; | ||||
|  | import org.mapstruct.ReportingPolicy; | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://yxk-admin.vip | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) | ||||
|  | public interface ${className}Mapper extends BaseMapper<${className}Dto, ${className}> { | ||||
|  | 
 | ||||
|  | } | ||||
| @ -0,0 +1,67 @@ | |||||
|  | 
 | ||||
|  | package ${package}.service.dto; | ||||
|  | 
 | ||||
|  | import lombok.Data; | ||||
|  | <#if queryHasTimestamp> | ||||
|  | import java.sql.Timestamp; | ||||
|  | </#if> | ||||
|  | <#if queryHasBigDecimal> | ||||
|  | import java.math.BigDecimal; | ||||
|  | </#if> | ||||
|  | <#if betweens??> | ||||
|  | import java.util.List; | ||||
|  | </#if> | ||||
|  | <#if queryColumns??> | ||||
|  | import me.zhengjie.annotation.Query; | ||||
|  | </#if> | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://el-admin.vip | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | @Data | ||||
|  | public class ${className}QueryCriteria{ | ||||
|  | <#if queryColumns??> | ||||
|  |     <#list queryColumns as column> | ||||
|  | 
 | ||||
|  | <#if column.queryType = '='> | ||||
|  |     /** 精确 */ | ||||
|  |     @Query | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  | </#if> | ||||
|  | <#if column.queryType = 'Like'> | ||||
|  |     /** 模糊 */ | ||||
|  |     @Query(type = Query.Type.INNER_LIKE) | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  | </#if> | ||||
|  | <#if column.queryType = '!='> | ||||
|  |     /** 不等于 */ | ||||
|  |     @Query(type = Query.Type.NOT_EQUAL) | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  | </#if> | ||||
|  | <#if column.queryType = 'NotNull'> | ||||
|  |     /** 不为空 */ | ||||
|  |     @Query(type = Query.Type.NOT_NULL) | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  | </#if> | ||||
|  | <#if column.queryType = '>='> | ||||
|  |     /** 大于等于 */ | ||||
|  |     @Query(type = Query.Type.GREATER_THAN) | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  | </#if> | ||||
|  | <#if column.queryType = '<='> | ||||
|  |     /** 小于等于 */ | ||||
|  |     @Query(type = Query.Type.LESS_THAN) | ||||
|  |     private ${column.columnType} ${column.changeColumnName}; | ||||
|  | </#if> | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  | <#if betweens??> | ||||
|  |     <#list betweens as column> | ||||
|  |     /** BETWEEN */ | ||||
|  |     @Query(type = Query.Type.BETWEEN) | ||||
|  |     private List<${column.columnType}> ${column.changeColumnName}; | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  | } | ||||
| @ -0,0 +1,26 @@ | |||||
|  | 
 | ||||
|  | package ${package}.repository; | ||||
|  | 
 | ||||
|  | import ${package}.domain.${className}; | ||||
|  | import org.springframework.data.jpa.repository.JpaRepository; | ||||
|  | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://yxk-admin.vip | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | public interface ${className}Repository extends JpaRepository<${className}, ${pkColumnType}>, JpaSpecificationExecutor<${className}> { | ||||
|  | <#if columns??> | ||||
|  |     <#list columns as column> | ||||
|  |         <#if column.columnKey = 'UNI'> | ||||
|  |     /** | ||||
|  |     * 根据 ${column.capitalColumnName} 查询 | ||||
|  |     * @param ${column.columnName} / | ||||
|  |     * @return / | ||||
|  |     */ | ||||
|  |     ${className} findBy${column.capitalColumnName}(${column.columnType} ${column.columnName}); | ||||
|  |         </#if> | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  | } | ||||
| @ -0,0 +1,69 @@ | |||||
|  | 
 | ||||
|  | package ${package}.service; | ||||
|  | 
 | ||||
|  | import ${package}.domain.${className}; | ||||
|  | import ${package}.service.dto.${className}Dto; | ||||
|  | import ${package}.service.dto.${className}QueryCriteria; | ||||
|  | import org.springframework.data.domain.Pageable; | ||||
|  | import java.util.Map; | ||||
|  | import java.util.List; | ||||
|  | import java.io.IOException; | ||||
|  | import javax.servlet.http.HttpServletResponse; | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://yxk-admin.vip | ||||
|  | * @description 服务接口 | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | public interface ${className}Service { | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |     * 查询数据分页 | ||||
|  |     * @param criteria 条件 | ||||
|  |     * @param pageable 分页参数 | ||||
|  |     * @return Map<String,Object> | ||||
|  |     */ | ||||
|  |     Map<String,Object> queryAll(${className}QueryCriteria criteria, Pageable pageable); | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |     * 查询所有数据不分页 | ||||
|  |     * @param criteria 条件参数 | ||||
|  |     * @return List<${className}Dto> | ||||
|  |     */ | ||||
|  |     List<${className}Dto> queryAll(${className}QueryCriteria criteria); | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |      * 根据ID查询 | ||||
|  |      * @param ${pkChangeColName} ID | ||||
|  |      * @return ${className}Dto | ||||
|  |      */ | ||||
|  |     ${className}Dto findById(${pkColumnType} ${pkChangeColName}); | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |     * 创建 | ||||
|  |     * @param resources / | ||||
|  |     * @return ${className}Dto | ||||
|  |     */ | ||||
|  |     ${className}Dto create(${className} resources); | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |     * 编辑 | ||||
|  |     * @param resources / | ||||
|  |     */ | ||||
|  |     void update(${className} resources); | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |     * 多选删除 | ||||
|  |     * @param ids / | ||||
|  |     */ | ||||
|  |     void deleteAll(${pkColumnType}[] ids); | ||||
|  | 
 | ||||
|  |     /** | ||||
|  |     * 导出数据 | ||||
|  |     * @param all 待导出的数据 | ||||
|  |     * @param response / | ||||
|  |     * @throws IOException / | ||||
|  |     */ | ||||
|  |     void download(List<${className}Dto> all, HttpServletResponse response) throws IOException; | ||||
|  | } | ||||
| @ -0,0 +1,143 @@ | |||||
|  | 
 | ||||
|  | package ${package}.service.impl; | ||||
|  | 
 | ||||
|  | import ${package}.domain.${className}; | ||||
|  | <#if columns??> | ||||
|  |     <#list columns as column> | ||||
|  |         <#if column.columnKey = 'UNI'> | ||||
|  |             <#if column_index = 1> | ||||
|  | import me.zhengjie.exception.EntityExistException; | ||||
|  |             </#if> | ||||
|  |         </#if> | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  | import com.yxkadmin.utils.ValidationUtil; | ||||
|  | import com.yxkadmin.utils.FileUtil; | ||||
|  | import lombok.RequiredArgsConstructor; | ||||
|  | import ${package}.repository.${className}Repository; | ||||
|  | import ${package}.service.${className}Service; | ||||
|  | import ${package}.service.dto.${className}Dto; | ||||
|  | import ${package}.service.dto.${className}QueryCriteria; | ||||
|  | import ${package}.service.mapstruct.${className}Mapper; | ||||
|  | import org.springframework.stereotype.Service; | ||||
|  | import org.springframework.transaction.annotation.Transactional; | ||||
|  | <#if !auto && pkColumnType = 'Long'> | ||||
|  | import cn.hutool.core.lang.Snowflake; | ||||
|  | import cn.hutool.core.util.IdUtil; | ||||
|  | </#if> | ||||
|  | <#if !auto && pkColumnType = 'String'> | ||||
|  | import cn.hutool.core.util.IdUtil; | ||||
|  | </#if> | ||||
|  | import org.springframework.data.domain.Page; | ||||
|  | import org.springframework.data.domain.Pageable; | ||||
|  | import com.yxkadmin.utils.PageUtil; | ||||
|  | import com.yxkadmin.utils.QueryHelp; | ||||
|  | import java.util.List; | ||||
|  | import java.util.Map; | ||||
|  | import java.io.IOException; | ||||
|  | import javax.servlet.http.HttpServletResponse; | ||||
|  | import java.util.ArrayList; | ||||
|  | import java.util.LinkedHashMap; | ||||
|  | 
 | ||||
|  | /** | ||||
|  | * @website https://yxk-admin.vip | ||||
|  | * @description 服务实现 | ||||
|  | * @author ${author} | ||||
|  | * @date ${date} | ||||
|  | **/ | ||||
|  | @Service | ||||
|  | @RequiredArgsConstructor | ||||
|  | public class ${className}ServiceImpl implements ${className}Service { | ||||
|  | 
 | ||||
|  |     private final ${className}Repository ${changeClassName}Repository; | ||||
|  |     private final ${className}Mapper ${changeClassName}Mapper; | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public Map<String,Object> queryAll(${className}QueryCriteria criteria, Pageable pageable){ | ||||
|  |         Page<${className}> page = ${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); | ||||
|  |         return PageUtil.toPage(page.map(${changeClassName}Mapper::toDto)); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public List<${className}Dto> queryAll(${className}QueryCriteria criteria){ | ||||
|  |         return ${changeClassName}Mapper.toDto(${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     @Transactional | ||||
|  |     public ${className}Dto findById(${pkColumnType} ${pkChangeColName}) { | ||||
|  |         ${className} ${changeClassName} = ${changeClassName}Repository.findById(${pkChangeColName}).orElseGet(${className}::new); | ||||
|  |         ValidationUtil.isNull(${changeClassName}.get${pkCapitalColName}(),"${className}","${pkChangeColName}",${pkChangeColName}); | ||||
|  |         return ${changeClassName}Mapper.toDto(${changeClassName}); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||
|  |     public ${className}Dto create(${className} resources) { | ||||
|  | <#if !auto && pkColumnType = 'Long'> | ||||
|  |         Snowflake snowflake = IdUtil.createSnowflake(1, 1); | ||||
|  |         resources.set${pkCapitalColName}(snowflake.nextId());  | ||||
|  | </#if> | ||||
|  | <#if !auto && pkColumnType = 'String'> | ||||
|  |         resources.set${pkCapitalColName}(IdUtil.simpleUUID());  | ||||
|  | </#if> | ||||
|  | <#if columns??> | ||||
|  |     <#list columns as column> | ||||
|  |     <#if column.columnKey = 'UNI'> | ||||
|  |         if(${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}()) != null){ | ||||
|  |             throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}()); | ||||
|  |         } | ||||
|  |     </#if> | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  |         return ${changeClassName}Mapper.toDto(${changeClassName}Repository.save(resources)); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||
|  |     public void update(${className} resources) { | ||||
|  |         ${className} ${changeClassName} = ${changeClassName}Repository.findById(resources.get${pkCapitalColName}()).orElseGet(${className}::new); | ||||
|  |         ValidationUtil.isNull( ${changeClassName}.get${pkCapitalColName}(),"${className}","id",resources.get${pkCapitalColName}()); | ||||
|  | <#if columns??> | ||||
|  |     <#list columns as column> | ||||
|  |         <#if column.columnKey = 'UNI'> | ||||
|  |         <#if column_index = 1> | ||||
|  |         ${className} ${changeClassName}1 = null; | ||||
|  |         </#if> | ||||
|  |         ${changeClassName}1 = ${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}()); | ||||
|  |         if(${changeClassName}1 != null && !${changeClassName}1.get${pkCapitalColName}().equals(${changeClassName}.get${pkCapitalColName}())){ | ||||
|  |             throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}()); | ||||
|  |         } | ||||
|  |         </#if> | ||||
|  |     </#list> | ||||
|  | </#if> | ||||
|  |         ${changeClassName}.copy(resources); | ||||
|  |         ${changeClassName}Repository.save(${changeClassName}); | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public void deleteAll(${pkColumnType}[] ids) { | ||||
|  |         for (${pkColumnType} ${pkChangeColName} : ids) { | ||||
|  |             ${changeClassName}Repository.deleteById(${pkChangeColName}); | ||||
|  |         } | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     @Override | ||||
|  |     public void download(List<${className}Dto> all, HttpServletResponse response) throws IOException { | ||||
|  |         List<Map<String, Object>> list = new ArrayList<>(); | ||||
|  |         for (${className}Dto ${changeClassName} : all) { | ||||
|  |             Map<String,Object> map = new LinkedHashMap<>(); | ||||
|  |         <#list columns as column> | ||||
|  |             <#if column.columnKey != 'PRI'> | ||||
|  |             <#if column.remark != ''> | ||||
|  |             map.put("${column.remark}", ${changeClassName}.get${column.capitalColumnName}()); | ||||
|  |             <#else> | ||||
|  |             map.put(" ${column.changeColumnName}",  ${changeClassName}.get${column.capitalColumnName}()); | ||||
|  |             </#if> | ||||
|  |             </#if> | ||||
|  |         </#list> | ||||
|  |             list.add(map); | ||||
|  |         } | ||||
|  |         FileUtil.downloadExcel(list, response); | ||||
|  |     } | ||||
|  | } | ||||
| @ -0,0 +1,27 @@ | |||||
|  | import request from '@/utils/request' | ||||
|  | 
 | ||||
|  | export function add(data) { | ||||
|  |   return request({ | ||||
|  |     url: 'api/${changeClassName}', | ||||
|  |     method: 'post', | ||||
|  |     data | ||||
|  |   }) | ||||
|  | } | ||||
|  | 
 | ||||
|  | export function del(ids) { | ||||
|  |   return request({ | ||||
|  |     url: 'api/${changeClassName}/', | ||||
|  |     method: 'delete', | ||||
|  |     data: ids | ||||
|  |   }) | ||||
|  | } | ||||
|  | 
 | ||||
|  | export function edit(data) { | ||||
|  |   return request({ | ||||
|  |     url: 'api/${changeClassName}', | ||||
|  |     method: 'put', | ||||
|  |     data | ||||
|  |   }) | ||||
|  | } | ||||
|  | 
 | ||||
|  | export default { add, edit, del } | ||||
| @ -0,0 +1,175 @@ | |||||
|  | <#--noinspection ALL--> | ||||
|  | <template> | ||||
|  |   <div class="app-container"> | ||||
|  |     <!--工具栏--> | ||||
|  |     <div class="head-container"> | ||||
|  |     <#if hasQuery> | ||||
|  |       <div v-if="crud.props.searchToggle"> | ||||
|  |         <!-- 搜索 --> | ||||
|  |         <#if queryColumns??> | ||||
|  |           <#list queryColumns as column> | ||||
|  |             <#if column.queryType != 'BetWeen'> | ||||
|  |         <label class="el-form-item-label"><#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if></label> | ||||
|  |         <el-input v-model="query.${column.changeColumnName}" clearable placeholder="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> | ||||
|  |             </#if> | ||||
|  |           </#list> | ||||
|  |         </#if> | ||||
|  |   <#if betweens??> | ||||
|  |     <#list betweens as column> | ||||
|  |       <#if column.queryType = 'BetWeen'> | ||||
|  |         <date-range-picker | ||||
|  |           v-model="query.${column.changeColumnName}" | ||||
|  |           start-placeholder="${column.changeColumnName}Start" | ||||
|  |           end-placeholder="${column.changeColumnName}Start" | ||||
|  |           class="date-item" | ||||
|  |         /> | ||||
|  |       </#if> | ||||
|  |     </#list> | ||||
|  |   </#if> | ||||
|  |         <rrOperation :crud="crud" /> | ||||
|  |       </div> | ||||
|  |     </#if> | ||||
|  |       <!--如果想在工具栏加入更多按钮,可以使用插槽方式, slot = 'left' or 'right'--> | ||||
|  |       <crudOperation :permission="permission" /> | ||||
|  |       <!--表单组件--> | ||||
|  |       <el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px"> | ||||
|  |         <el-form ref="form" :model="form" <#if isNotNullColumns??>:rules="rules"</#if> size="small" label-width="80px"> | ||||
|  |     <#if columns??> | ||||
|  |       <#list columns as column> | ||||
|  |         <#if column.formShow> | ||||
|  |           <el-form-item label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>"<#if column.istNotNull> prop="${column.changeColumnName}"</#if>> | ||||
|  |             <#if column.formType = 'Input'> | ||||
|  |             <el-input v-model="form.${column.changeColumnName}" style="width: 370px;" /> | ||||
|  |             <#elseif column.formType = 'Textarea'> | ||||
|  |             <el-input v-model="form.${column.changeColumnName}" :rows="3" type="textarea" style="width: 370px;" /> | ||||
|  |             <#elseif column.formType = 'Radio'> | ||||
|  |               <#if (column.dictName)?? && (column.dictName)!=""> | ||||
|  |             <el-radio v-model="form.${column.changeColumnName}" v-for="item in dict.${column.dictName}" :key="item.id" :label="item.value">{{ item.label }}</el-radio> | ||||
|  |               <#else> | ||||
|  |                 未设置字典,请手动设置 Radio | ||||
|  |               </#if> | ||||
|  |             <#elseif column.formType = 'Select'> | ||||
|  |               <#if (column.dictName)?? && (column.dictName)!=""> | ||||
|  |             <el-select v-model="form.${column.changeColumnName}" filterable placeholder="请选择"> | ||||
|  |               <el-option | ||||
|  |                 v-for="item in dict.${column.dictName}" | ||||
|  |                 :key="item.id" | ||||
|  |                 :label="item.label" | ||||
|  |                 :value="item.value" /> | ||||
|  |             </el-select> | ||||
|  |               <#else> | ||||
|  |             未设置字典,请手动设置 Select | ||||
|  |               </#if> | ||||
|  |             <#else> | ||||
|  |             <el-date-picker v-model="form.${column.changeColumnName}" type="datetime" style="width: 370px;" /> | ||||
|  |             </#if> | ||||
|  |           </el-form-item> | ||||
|  |         </#if> | ||||
|  |       </#list> | ||||
|  |     </#if> | ||||
|  |         </el-form> | ||||
|  |         <div slot="footer" class="dialog-footer"> | ||||
|  |           <el-button type="text" @click="crud.cancelCU">取消</el-button> | ||||
|  |           <el-button :loading="crud.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> | ||||
|  |         </div> | ||||
|  |       </el-dialog> | ||||
|  |       <!--表格渲染--> | ||||
|  |       <el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler"> | ||||
|  |         <el-table-column type="selection" width="55" /> | ||||
|  |         <#if columns??> | ||||
|  |             <#list columns as column> | ||||
|  |             <#if column.columnShow> | ||||
|  |           <#if (column.dictName)?? && (column.dictName)!=""> | ||||
|  |         <el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>"> | ||||
|  |           <template slot-scope="scope"> | ||||
|  |             {{ dict.label.${column.dictName}[scope.row.${column.changeColumnName}] }} | ||||
|  |           </template> | ||||
|  |         </el-table-column> | ||||
|  |           <#elseif column.columnType != 'Timestamp'> | ||||
|  |         <el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>" /> | ||||
|  |                 <#else> | ||||
|  |         <el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>"> | ||||
|  |           <template slot-scope="scope"> | ||||
|  |             <span>{{ parseTime(scope.row.${column.changeColumnName}) }}</span> | ||||
|  |           </template> | ||||
|  |         </el-table-column> | ||||
|  |                 </#if> | ||||
|  |             </#if> | ||||
|  |             </#list> | ||||
|  |         </#if> | ||||
|  |         <el-table-column v-permission="['admin','${changeClassName}:edit','${changeClassName}:del']" label="操作" width="150px" align="center"> | ||||
|  |           <template slot-scope="scope"> | ||||
|  |             <udOperation | ||||
|  |               :data="scope.row" | ||||
|  |               :permission="permission" | ||||
|  |             /> | ||||
|  |           </template> | ||||
|  |         </el-table-column> | ||||
|  |       </el-table> | ||||
|  |       <!--分页组件--> | ||||
|  |       <pagination /> | ||||
|  |     </div> | ||||
|  |   </div> | ||||
|  | </template> | ||||
|  | 
 | ||||
|  | <script> | ||||
|  | import crud${className} from '@/api/${changeClassName}' | ||||
|  | import CRUD, { presenter, header, form, crud } from '@crud/crud' | ||||
|  | import rrOperation from '@crud/RR.operation' | ||||
|  | import crudOperation from '@crud/CRUD.operation' | ||||
|  | import udOperation from '@crud/UD.operation' | ||||
|  | import pagination from '@crud/Pagination' | ||||
|  | 
 | ||||
|  | const defaultForm = { <#if columns??><#list columns as column>${column.changeColumnName}: null<#if column_has_next>, </#if></#list></#if> } | ||||
|  | export default { | ||||
|  |   name: '${className}', | ||||
|  |   components: { pagination, crudOperation, rrOperation, udOperation }, | ||||
|  |   mixins: [presenter(), header(), form(defaultForm), crud()], | ||||
|  |   <#if hasDict> | ||||
|  |   dicts: [<#if hasDict??><#list dicts as dict>'${dict}'<#if dict_has_next>, </#if></#list></#if>], | ||||
|  |   </#if> | ||||
|  |   cruds() { | ||||
|  |     return CRUD({ title: '${apiAlias}', url: 'api/${changeClassName}', idField: '${pkChangeColName}', sort: '${pkChangeColName},desc', crudMethod: { ...crud${className} }}) | ||||
|  |   }, | ||||
|  |   data() { | ||||
|  |     return { | ||||
|  |       permission: { | ||||
|  |         add: ['admin', '${changeClassName}:add'], | ||||
|  |         edit: ['admin', '${changeClassName}:edit'], | ||||
|  |         del: ['admin', '${changeClassName}:del'] | ||||
|  |       }, | ||||
|  |       rules: { | ||||
|  |         <#if isNotNullColumns??> | ||||
|  |         <#list isNotNullColumns as column> | ||||
|  |         <#if column.istNotNull> | ||||
|  |         ${column.changeColumnName}: [ | ||||
|  |           { required: true, message: '<#if column.remark != ''>${column.remark}</#if>不能为空', trigger: 'blur' } | ||||
|  |         ]<#if column_has_next>,</#if> | ||||
|  |         </#if> | ||||
|  |         </#list> | ||||
|  |         </#if> | ||||
|  |       }<#if hasQuery>, | ||||
|  |       queryTypeOptions: [ | ||||
|  |         <#if queryColumns??> | ||||
|  |         <#list queryColumns as column> | ||||
|  |         <#if column.queryType != 'BetWeen'> | ||||
|  |         { key: '${column.changeColumnName}', display_name: '<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>' }<#if column_has_next>,</#if> | ||||
|  |         </#if> | ||||
|  |         </#list> | ||||
|  |         </#if> | ||||
|  |       ] | ||||
|  |       </#if> | ||||
|  |     } | ||||
|  |   }, | ||||
|  |   methods: { | ||||
|  |     // 钩子:在获取表格数据之前执行,false 则代表不获取数据 | ||||
|  |     [CRUD.HOOK.beforeRefresh]() { | ||||
|  |       return true | ||||
|  |     } | ||||
|  |   } | ||||
|  | } | ||||
|  | </script> | ||||
|  | 
 | ||||
|  | <style scoped> | ||||
|  | 
 | ||||
|  | </style> | ||||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue