## 代碼層級梳理
#### 代碼層級

#### annotation層

###### ExcelVoAttribut
主要用于寫@ExcelVoAttribute注解,可以更靈活的配置Excel數據表導出的一些基本配置
###### LogDirection
主要用于寫@LogDirection注解,注解里包含一個字段(接口描述),通過接口添加這個注解,填寫上接口的功能,當用戶在訪問接口時候,可以利用aop技術,把用戶的操作記到日志里
#### aspect層

###### LogDrectionAspect
主要定義了aop切點的位置和增強的方法
```java
/**
* @Author Johnny
* @Date 2020/4/16
* @Version 1.0
*/
@Aspect
@Component
@Slf4j
public class LogDirectionAspect {
? ? @Pointcut("@annotation(logDirection)")
? ? public void doLogDirection(LogDirection logDirection){
? ? }
? ? @Around(value = "doLogDirection(logDirection)",argNames = "pjp,logDirection")
? ? public Object doBefore(ProceedingJoinPoint pjp,LogDirection logDirection) throws? Throwable{
? ? ? ? if (!"".equals(logDirection.direction())) {
? ? ? ? ? ? log.info(logDirection.direction()+"接口被調用");
? ? ? ? ? ? return? pjp.proceed();
? ? ? ? }
? ? ? ? return "接口信息不規范";
? ? }
}
```
@Pointcut定義切點,此處定義切點為使用@LogDirection的方法
@Around定義增強的方法,通過around的參數ProceedingJoinPoint可以很好的控制程序能不能繼續執行下一步,在權限判定發揮著比較重要的作用
拓展:
利用下面的這種方法,需要在xml文件中配置bean實例,當然也可以在方法中通過手動掃包的方法獲得bean實例進行操作
```
/**
* @Author Johnny
* @Date 2020/4/16
* @Version 1.0
*/
@Aspect
public class MyAdvice {
? ? /**
? ? * 配置切點
? ? */
? ? @Pointcut("execution(* com.soft1851.spring.web.dao..*.insert*(..))")
? ? public void pointCut() {
? ? }
? ? /**
? ? * 配置前置增強
? ? */
? ? @Before("MyAdvice.pointCut()")
? ? public void beforeMethod() {
? ? ? ? System.out.println("等待數據插入");
? ? }
? ? /**
? ? * 配置后置增強
? ? */
? ? @AfterReturning("execution(* com.soft1851.spring.web.dao..*.*(..))")
? ? public void afterMethod(){
? ? ? ? System.out.println("關閉數據庫");
? ? }
}
```
xml配置實例
```
<bean name="forumDao" class="com.soft1851.spring.web.dao.impl.ForumDaoImpl"/>
? ? <bean name="myAdvice" class="com.soft1851.spring.web.interceptor.MyAdvice"/>
? ? <aop:aspectj-autoproxy/>
```
#### common層

ResponseResult和ResultCode
這兩個類可以將響應體統一進行封裝,將接口的響應體規范化,使前端獲取數據也更加友好
貼下代碼,防止以后找不到
ResponseResult類
```
package com.soft1851.music.admin.common;
import lombok.Data;
import java.io.Serializable;
/**
* @author Johnny
* @Date: 2020/4/21 19:52
* @Description:
*/
@Data
public class ResponseResult implements Serializable {
? ? private static final long serialVersionUID = -3948389268046368059L;
? ? private Integer code;
? ? private String msg;
? ? private Object data;
? ? private ResponseResult() {
? ? }
? ? public ResponseResult(Integer code, String msg) {
? ? ? ? this.code = code;
? ? ? ? this.msg = msg;
? ? }
? ? public static ResponseResult success() {
? ? ? ? ResponseResult result = new ResponseResult();
? ? ? ? result.setResultCode(ResultCode.SUCCESS);
? ? ? ? return result;
? ? }
? ? public static ResponseResult success(Object data) {
? ? ? ? ResponseResult result = new ResponseResult();
? ? ? ? result.setResultCode(ResultCode.SUCCESS);
? ? ? ? result.setData(data);
? ? ? ? return result;
? ? }
? ? public static ResponseResult failure(ResultCode resultCode) {
? ? ? ? ResponseResult result = new ResponseResult();
? ? ? ? result.setResultCode(resultCode);
? ? ? ? return result;
? ? }
? ? public static ResponseResult failure(ResultCode resultCode, Object data) {
? ? ? ? ResponseResult result = new ResponseResult();
? ? ? ? result.setResultCode(resultCode);
? ? ? ? result.setData(data);
? ? ? ? return result;
? ? }
? ? public void setResultCode(ResultCode code) {
? ? ? ? this.code = code.code();
? ? ? ? this.msg = code.message();
? ? }
}
```
ResultCode類(枚舉)
```
package com.soft1851.music.admin.common;
/**
* @author Johnny
* @Date: 2020/4/21 19:52
* @Description:
*/
public enum ResultCode {
? ? /* 成功狀態碼 */
? ? SUCCESS(1, "成功"),
? ? /* 通用錯誤:10001-19999 */
? ? PARAM_IS_INVALID(10001, "參數無效"),
? ? PARAM_IS_BLANK(10002, "參數為空"),
? ? PARAM_TYPE_BIND_ERROR(10003, "參數類型錯誤"),
? ? PARAM_NOT_COMPLETE(10004, "參數缺失"),
? ? HTTP_METHOD_ERROR(10005, "請求方法錯誤"),
? ? HTTP_METHOD_NOT_ALLOWED(10006, "請求方法不允許"),
? ? HTTP_NOT_FOUND(10007, "請求地址錯誤"),
? ? BOUND_STATEMENT_NOT_FOUND(10008, "Mybatis未綁定"),
? ? CONNECTION_ERROR(10009, "網絡連接錯誤"),
? ? ARITHMETIC_ERROR(100010, "計算錯誤"),
? ? /* 用戶錯誤:20001-29999*/
? ? USER_NOT_SIGN_IN(20001, "請先登錄"),
? ? USER_PASSWORD_ERROR(20002, "密碼錯誤"),
? ? USER_ACCOUNT_ERROR(20003, "賬號錯誤"),
? ? USER_VERIFY_CODE_ERROR(20004, "驗證碼錯誤"),
? ? USER_CODE_TIMEOUT(20005, "驗證碼失效"),
? ? USER_ACCOUNT_FORBIDDEN(20006, "賬號已被禁用"),
? ? USER_SIGN_UP_FAIL(20007, "用戶注冊失敗"),
? ? USER_SIGN_IN_FAIL(20008, "用戶登錄失敗"),
? ? USER_NOT_FOUND(20009, "用戶不存在"),
? ? USER_NO_AUTH(20019, "用戶權限不足"),
? ? USER_TOKEN_EXPIRES(200010, "Token已過期"),
? ? /* 業務錯誤:30001-39999 */
? ? SMS_ERROR(30001, "短信業務出現問題"),
? ? UPLOAD_ERROR(30002, "上傳文件業務出現問題"),
? ? CAPTCHA_ERROR(30003, "驗證碼業務出現問題"),
? ? /* 數據錯誤:40001-49999 */
? ? RESULT_CODE_DATA_NONE(50001, "數據未找到"),
? ? DATA_IS_WRONG(50002, "數據有誤"),
? ? DATA_ALREADY_EXISTED(50003, "數據已存在"),
? ? DATABASE_ERROR(50004, "數據庫操作異常"),
? ? /* 服務器或系統錯誤:50001-599999 */
? ? SERVER_ERROR(50000, "服務器錯誤,請稍后重試"),
? ? SYSTEM_ERROR(40001, "系統錯誤,請稍后重試"),
? ? /* 接口錯誤:60001-69999 */
? ? INTERFACE_INNER_INVOKE_ERROR(60001, "內部系統接口調用異常"),
? ? INTERFACE_OUTER_INVOKE_ERROR(60002, "外部系統接口調用異常"),
? ? INTERFACE_FORBID_VISIT(60003, "該接口禁止訪問"),
? ? INTERFACE_ADDRESS_INVALID(60004, "接口地址無效"),
? ? INTERFACE_REQUEST_TIMEOUT(60005, "接口請求超時"),
? ? INTERFACE_EXCEED_LOAD(60006, "接口負載過高"),
? ? /* 權限錯誤:70001-79999 */
? ? PERMISSION_NO_ACCESS(70001,"無訪問權限");
? ? private Integer code;
? ? private String message;
? ? ResultCode(Integer code, String message) {
? ? ? ? this.code = code;
? ? ? ? this.message = message;
? ? }
? ? public Integer code() {
? ? ? ? return this.code;
? ? }
? ? public String message() {
? ? ? ? return this.message;
? ? }
? ? public static String getMessage(String name) {
? ? ? ? for (ResultCode item : ResultCode.values()) {
? ? ? ? ? ? if (item.name().equals(name)) {
? ? ? ? ? ? ? ? return item.message;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return name;
? ? }
? ? public static Integer getCode(String name) {
? ? ? ? for (ResultCode item : ResultCode.values()) {
? ? ? ? ? ? if (item.name().equals(name)) {
? ? ? ? ? ? ? ? return item.code;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return null;
? ? }
? ? @Override
? ? public String toString() {
? ? ? ? return this.name();
? ? }
}
```
#### config層

###### CaptchaConfig
主要是為kaptcha谷歌驗證碼的一些基本信息進行配置,比如字體,驗證碼位數等等
附上一些kaptha的基本配置信息
| **Constant**? ? ? ? ? ? ? ? ? ? | **描述**? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | **默認值**? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| -------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------- |
| kaptcha.border? ? ? ? ? ? ? ? ? | 圖片邊框,合法值:yes , no? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | yes? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.border.color? ? ? ? ? ? | 邊框顏色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.border.thickness? ? ? ? | 邊框厚度,合法值:>0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | 1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.image.width? ? ? ? ? ? ? | 圖片寬? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | 200? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.image.height? ? ? ? ? ? | 圖片高? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | 50? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.producer.impl? ? ? ? ? ? | 圖片實現類? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | com.google.code.kaptcha.impl.DefaultKaptcha? ? ? ? ? |
| kaptcha.textproducer.impl? ? ? ? | 文本實現類? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | com.google.code.kaptcha.text.impl.DefaultTextCreator? |
| kaptcha.textproducer.char.string | 文本集合,驗證碼值從此集合中獲取? ? ? ? ? ? ? ? ? ? ? ? ? ? | abcde2345678gfynmnpwx? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.textproducer.char.length | 驗證碼長度? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | 5? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.textproducer.font.names? | 字體? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | Arial, Courier? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.textproducer.font.size? | 字體大小? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | 40px.? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.textproducer.font.color? | 字體顏色,合法值: r,g,b? 或者 white,black,blue.? ? ? ? ? ? | black? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.textproducer.char.space? | 文字間隔? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | 2? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.noise.impl? ? ? ? ? ? ? | 干擾實現類? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | com.google.code.kaptcha.impl.DefaultNoise? ? ? ? ? ? |
| kaptcha.noise.color? ? ? ? ? ? ? | 干擾 顏色,合法值: r,g,b 或者 white,black,blue.? ? ? ? ? ? | black? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.obscurificator.impl? ? ? | 圖片樣式:? 水紋com.google.code.kaptcha.impl.WaterRipple? 魚眼com.google.code.kaptcha.impl.FishEyeGimpy? 陰影com.google.code.kaptcha.impl.ShadowGimpy | com.google.code.kaptcha.impl.WaterRipple? ? ? ? ? ? ? |
| kaptcha.background.impl? ? ? ? ? | 背景實現類? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | com.google.code.kaptcha.impl.DefaultBackground? ? ? ? |
| kaptcha.background.clear.from? ? | 背景顏色漸變,開始顏色? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | light grey? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.background.clear.to? ? ? | 背景顏色漸變, 結束顏色? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | white? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.word.impl? ? ? ? ? ? ? ? | 文字渲染器? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
| kaptcha.session.key? ? ? ? ? ? ? | session key? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | KAPTCHA_SESSION_KEY? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| kaptcha.session.date? ? ? ? ? ? | session date? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | KAPTCHA_SESSION_DATE? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
###### CorsConfig
主要是為跨域做一些基本的配置,學習階段后端必備
代碼展示,防止以后忘記
```
/**
* @Author Johnny
* @Date 2020/4/16
* @Version 1.0
*/
@Configuration
public class CorsConfig {
? ? @Bean
? ? public FilterRegistrationBean<CorsFilter> corsFilter() {
? ? ? ? UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
? ? ? ? CorsConfiguration config = new CorsConfiguration();
? ? ? ? config.setAllowCredentials(true);
? ? ? ? //放行所有跨域的客戶端domain
? ? ? ? config.addAllowedOrigin("*");
? ? ? ? //允許的請求方法列表
? ? ? ? String[] requestMethods = {"GET", "POST", "PUT", "DELETE", "OPTIONS"};
? ? ? ? List<String> allowedRequestMethods = Arrays.asList(requestMethods);
? ? ? ? config.setAllowedMethods(allowedRequestMethods);
? ? ? ? //允許的客戶端請求頭列表
? ? ? ? String[] requestHeaders = {"x-requested-with", "Content-Type", "Authorization"};
? ? ? ? List<String> allowedHeaders = Arrays.asList(requestHeaders);
? ? ? ? config.setAllowedHeaders(allowedHeaders);
? ? ? ? //允許的響應頭列表
? ? ? ? String[] responseHeaders = {"Access-Control-Expose-Headers", "Authorization"};
? ? ? ? List<String> allowedExposedHeaders = Arrays.asList(responseHeaders);
? ? ? ? config.setExposedHeaders(allowedExposedHeaders);
? ? ? ? source.registerCorsConfiguration("/**", config);
? ? ? ? FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
? ? ? ? // 這個順序很重要,設置在最前
? ? ? ? bean.setOrder(0);
? ? ? ? return bean;
? ? }
}
```
###### MybatisPlusConfig
主要是為mybatisplus做一些基本配置(如果要有分頁查詢數據,則這個配置文件必須要有)
```
/**
* @Author Johnny
* @Date 2020/4/16
* @Version 1.0
*/
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
? ? @Bean
? ? public PaginationInterceptor paginationInterceptor() {
? ? ? ? PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
? ? ? ? //設置請求的頁面大于最大頁操作,true調回到首頁,false繼續請求,默認為false
? ? ? ? paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
? ? ? ? return paginationInterceptor;
? ? }
}
```
###### MySqlGenerator
代碼生成器,功能強大無比,根據數據表,一鍵生成各種層級,各種實體
```
/**
* @ClassName MySqlGenerator
* @Description 代碼生成器
* @Author Johnny
* @Date 2020/4/16
* @Version 1.0
*/
@Slf4j
public class MySqlGenerator {
? ? public static void main(String[] args) {
? ? ? ? //全局策略配置
? ? ? ? GlobalConfig config = new GlobalConfig();
? ? ? ? //獲得當前項目根路徑d:/dev/SpringBoot/spring-boot-learning
? ? ? ? String projectPath = System.getProperty("user.dir");
? ? ? ? config.setActiveRecord(true)
? ? ? ? ? ? ? ? //作者注釋
? ? ? ? ? ? ? ? .setAuthor("Johnny")
? ? ? ? ? ? ? ? //代碼生成輸出路徑
? ? ? ? ? ? ? ? .setOutputDir(projectPath + "/src/main/java")
? ? ? ? ? ? ? ? //覆蓋已有文件,默認false
? ? ? ? ? ? ? ? .setFileOverride(true)
? ? ? ? ? ? ? ? //是否打開輸出目錄窗口。默認true
? ? ? ? ? ? ? ? .setOpen(false)
? ? ? ? ? ? ? ? //開啟swagger2模式
? ? ? ? ? ? ? ? //.setSwagger2(true)
? ? ? ? ? ? ? ? //開啟ActiveRecord模式
? ? ? ? ? ? ? ? .setActiveRecord(true)
? ? ? ? ? ? ? ? //mapper添加restMap
? ? ? ? ? ? ? ? .setBaseResultMap(true)
? ? ? ? ? ? ? ? //mapper添加Base_Column_List
? ? ? ? ? ? ? ? .setBaseColumnList(true)
? ? ? ? ? ? ? ? //時間類型對應策略,默認time_pack
? ? ? ? ? ? ? ? //.setDateType(DateType.TIME_PACK)
? ? ? ? ? ? ? ? //相關包中的接口和類名后綴
? ? ? ? ? ? ? ? .setMapperName("%sMapper")
? ? ? ? ? ? ? ? .setServiceName("%sService")
? ? ? ? ? ? ? ? .setServiceImplName("%sServiceImpl");
? ? ? ? //數據庫表配置,通過該配置,可指定需要生成哪些表或者排除哪些表
? ? ? ? StrategyConfig strategyConfig = new StrategyConfig();
? ? ? ? //是否大寫命名
? ? ? ? strategyConfig.setCapitalMode(true)
? ? ? ? ? ? ? ? //是否跳過視圖
? ? ? ? ? ? ? ? .setSkipView(true)
? ? ? ? ? ? ? ? //數據庫表映射到實體的命名策略為駝峰式
? ? ? ? ? ? ? ? .setNaming(NamingStrategy.underline_to_camel)
? ? ? ? ? ? ? ? //生成表,可以寫多個,如果不加參數,默認為所有表
? ? ? ? ? ? ? ? .setInclude()
? ? ? ? ? ? ? ? .setEntityBuilderModel(true)
? ? ? ? ? ? ? ? .setEntityLombokModel(true)
? ? ? ? ? ? ? ? .setRestControllerStyle(true)
? ? ? ? ? ? ? ? .setEntityTableFieldAnnotationEnable(true);
? ? ? ? //包名配置
? ? ? ? PackageConfig packageConfig = new PackageConfig();
? ? ? ? //父包名
? ? ? ? packageConfig.setParent("com.soft1851.music.admin")
? ? ? ? ? ? ? ? .setMapper("mapper")
? ? ? ? ? ? ? ? .setService("service")
? ? ? ? ? ? ? ? .setController("controller")
? ? ? ? ? ? ? ? .setXml("xml")
? ? ? ? ? ? ? ? .setEntity("entity");
? ? ? ? //數據源配置
? ? ? ? DataSourceConfig dataSourceConfig = new DataSourceConfig();
? ? ? ? dataSourceConfig.setDbType(DbType.MYSQL)
? ? ? ? ? ? ? ? .setDriverName("com.mysql.jdbc.Driver")
? ? ? ? ? ? ? ? .setUrl("jdbc:mysql://rm-m5ee476bu350735gjeo.mysql.rds.aliyuncs.com:3306/cloud_music?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true")
? ? ? ? ? ? ? ? .setUsername("root")
? ? ? ? ? ? ? ? .setPassword("XuNiit_#");
? ? ? ? AutoGenerator autoGenerator = new AutoGenerator();
? ? ? ? autoGenerator.setGlobalConfig(config)
? ? ? ? ? ? ? ? .setStrategy(strategyConfig)
? ? ? ? ? ? ? ? .setDataSource(dataSourceConfig)
? ? ? ? ? ? ? ? .setTemplateEngine(new FreemarkerTemplateEngine())
? ? ? ? ? ? ? ? .setPackageInfo(packageConfig);
? ? ? ? autoGenerator.execute();
? ? ? ? log.info("=============代碼生成成功================");
? ? }
}
```
###### RedisConfig
主要對RedisConfig緩存數據庫進行一些基本配置
```
/**
* @author Johnny
* @Date: 2020/4/21 20:45
* @Description:
*/
@Configuration
@EnableCaching
public class RedisConfig {
? ? @Bean
? ? CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
? ? ? ? //初始化RedisCacheWrite
? ? ? ? RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
? ? ? ? RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
? ? ? ? //設置默認過期時間為1天
? ? ? ? defaultCacheConfig.entryTtl(Duration.ofDays(1));
? ? ? ? //初始化RedisCacheManger
? ? ? ? return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
? ? }
? ? @Bean
? ? public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
? ? ? ? RedisTemplate<Object,Object> template = new RedisTemplate<>();
? ? ? ? template.setConnectionFactory(connectionFactory);
? ? ? ? //使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值(默認使用JDK的序列化方式)
? ? ? ? Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
? ? ? ? ObjectMapper mapper = new ObjectMapper();
? ? ? ? mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
? ? ? ? //使用stringRedisSerializer來序列化和反序列化redis的value值
? ? ? ? template.setKeySerializer(new StringRedisSerializer());
? ? ? ? template.afterPropertiesSet();
? ? ? ? return template;
? ? }
? ? @Bean
? ? public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
? ? ? ? StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
? ? ? ? stringRedisTemplate.setConnectionFactory(factory);
? ? ? ? return stringRedisTemplate;
? ? }
}
```
###### WebConfig
這個類中主要配置了攔截器,可以對部分頁面進行統一攔截,當然這個只是統一的攔截,具體的攔截所要進行的操作,還在下文
```
/**
* @author Johnny
* @Date: 2020/4/21 20:58
* @Description:
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
? ? @Resource
? ? private LoginInterceptor loginInterceptor;
? ? @Resource
? ? private JwtInterceptor jwtInterceptor;
? ? /**
? ? * 添加攔截器配置
? ? */
? ? @Override
? ? public void addInterceptors(InterceptorRegistry registry) {
? ? ? ? //攔截路徑可自行配置多個 可用 ,分隔開
? ? ? ? registry.addInterceptor(loginInterceptor).addPathPatterns("/sysAdmin/login").excludePathPatterns("/**").excludePathPatterns("/static/**");
//? ? ? ? registry.addInterceptor(jwtInterceptor).addPathPatterns("/**").excludePathPatterns("/sysAdmin/login", "/captcha","/songList/page").excludePathPatterns("/static/**");
? ? }
}
```
#### controller層
controller層暫時帶過,后面有時間的話把controller層常用注解匯總整理下
#### dto層
目前還沒有將entity、vo、dto統一放到domain里,dto里主要是用戶請求接口時需要傳的參數,vo主要時后端返回前端展示的參數,而entity就是我們和數據庫表所吻合的類。舉例:我們數據庫中的用戶表的字段肯定不只時賬號和密碼,會有一些其他字段,比如加密鹽、用戶基本信息等等,但是用戶請求登錄接口時,只需要傳賬號和密碼就可以了,此時此刻,為了防止資源的浪費,我們就定義一個dto來接收前端傳來的參數。當用戶進入主界面時,查看個人信息,后端并不需要將用戶id、加密鹽等私密信息展示到前端,此時,同樣為了節省資源,我們定義vo視圖對象,將前端需要的參數放進去,再傳入前端。
#### entity層
entity層主要是將數據庫中的表一對一的寫好,此處是直接用代碼生成器生成
#### exception

###### CustomException
定義全局異常處理,使用時直接throws CustomException就好
```
/**
* @author Johnny
* @Date: 2020/4/22 10:48
* @Description:
*/
public class CustomException extends? RuntimeException {
? ? protected ResultCode resultCode;
? ? public CustomException(String msg, ResultCode resultCode) {
? ? ? ? super(msg);
? ? ? ? this.resultCode = resultCode;
? ? }
? ? public ResultCode getResultCode() {
? ? ? ? return resultCode;
? ? }
}
```
###### GlobalException
也是全局異常處理,但和上面略有區別,非手動throw,spring在遇到異常時,會自動來這個類里查找,如果已經有配置過的,就會按照配置要求輸出內容
```
/**
* @author Johnny
* @Date: 2020/4/30 18:45
* @Description:
*/
@ControllerAdvice
public class GlobalException {
? ? @ExceptionHandler(ConstraintViolationException.class)
? ? ResponseResult handleConstraintViolationException(ConstraintViolationException e) {
? ? ? ? return ResponseResult.failure(ResultCode.DATA_IS_WRONG);
? ? }
}
```
###### JwtException
同CustomException類格式一樣,但這個側重于處理Jwt模塊的異常
#### filter

###### ChannelFilter
```
/**
* @author Johnny
* @Date: 2020/4/21 20:19
* @Description:
*/
@WebFilter(urlPatterns = "/*",filterName = "channelFilter")
public class ChannelFilter implements Filter {
? ? @Override
? ? public void init(FilterConfig filterConfig) throws ServletException {
? ? }
? ? @Override
? ? public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
? ? ? ? ServletRequest requestWrapper = null;
? ? ? ? if (servletRequest instanceof HttpServletRequest) {
? ? ? ? ? ? requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
? ? ? ? }
? ? ? ? if (requestWrapper == null) {
? ? ? ? ? ? filterChain.doFilter(servletRequest,servletResponse);
? ? ? ? }else{
? ? ? ? ? ? filterChain.doFilter(requestWrapper, servletResponse);
? ? ? ? }
? ? }
? ? @Override
? ? public void destroy() {
? ? }
}
```
###### LoginFilter
這個類主要針對用戶登錄進行過濾
```
/**
* @author Johnny
* @Date: 2020/4/26 10:46
* @Description:
*/
@Slf4j
@WebFilter(urlPatterns = "/login",filterName = "LoginFilter")
public class LoginFilter implements Filter {
? ? @Override
? ? public void init(FilterConfig filterConfig) throws ServletException {
? ? }
? ? @Override
? ? public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
? ? ? ? ServletRequest requestWrapper = null;
? ? ? ? if (servletRequest instanceof HttpServletRequest) {
? ? ? ? ? ? String url = ((HttpServletRequest) servletRequest).getRequestURI();
? ? ? ? ? ? //判斷接口是否為導入接口
? ? ? ? ? ? if ("/resource/guide".equals(url)) {
? ? ? ? ? ? ? ? Part file = ((HttpServletRequest) servletRequest).getPart("file");
? ? ? ? ? ? ? ? log.info("文件名:" + file);
? ? ? ? ? ? }
? ? ? ? ? ? requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
? ? ? ? }
? ? ? ? if (requestWrapper == null) {
? ? ? ? ? ? filterChain.doFilter(servletRequest, servletResponse);
? ? ? ? }else{
? ? ? ? ? ? filterChain.doFilter(requestWrapper, servletResponse);
? ? ? ? }
? ? }
? ? @Override
? ? public void destroy() {
? ? }
}
```
#### handle層

###### GlobalExceptionHandle
定義全局異常
```
/**
* @author Johnny
* @Date: 2020/4/22 10:40
* @Description:
*/
@RestControllerAdvice(annotations = {RestController.class, Controller.class})
@Slf4j
public class GlobalExceptionHandle {
? ? //自定義異常
? ? /**
? ? * JwtException
? ? * @param exception
? ? * @return
? ? */
? ? @ExceptionHandler(value = {JwtException.class})
? ? @ResponseBody
? ? public ResponseResult sendError(JwtException exception) {
? ? ? ? log.error(exception.getMessage());
? ? ? ? return ResponseResult.failure(exception.getResultCode());
? ? }
? ? /**
? ? * CustomException
? ? * @param exception
? ? * @return
? ? */
? ? @ExceptionHandler(value = {CustomException.class})
? ? @ResponseBody
? ? public ResponseResult sendError(CustomException exception) {
? ? ? ? log.error(exception.getMessage());
? ? ? ? return ResponseResult.failure(exception.getResultCode());
? ? }
? ? //系統級異常
? ? /**
? ? * InvalidClassException
? ? * @param exception
? ? * @return
? ? */
? ? @ExceptionHandler(value = {InvalidClassException.class})
? ? @ResponseBody
? ? public ResponseResult sendError(InvalidClaimException exception) {
? ? ? ? log.error(exception.getMessage());
? ? ? ? //返回token已經過期
? ? ? ? return ResponseResult.failure(ResultCode.USER_TOKEN_EXPIRES);
? ? }
? ? /**
? ? * NullPointerException
? ? * @param exception
? ? * @return
? ? */
? ? @ExceptionHandler(value = {NullPointerException.class})
? ? @ResponseBody
? ? public ResponseResult sendError(NullPointerException exception) {
? ? ? ? log.error(exception.getMessage());
? ? ? ? //空指針提示數據未找到
? ? ? ? return ResponseResult.failure(ResultCode.RESULT_CODE_DATA_NONE);
? ? }
? ? /**
? ? * IOException
? ? * @param exception
? ? * @return
? ? */
? ? @ExceptionHandler(value = {IOException.class})
? ? @ResponseBody
? ? public ResponseResult sendError(IOException exception) {
? ? ? ? log.error(exception.getMessage());
? ? ? ? //io異常提示驗證碼出現問題
? ? ? ? return ResponseResult.failure(ResultCode.CAPTCHA_ERROR);
? ? }
```
###### GlobalResponseHandle
處理全局的響應頭(當接口返回類型非ResponseResult類型時,可以自動轉換)
```
/**
* @author Johnny
* @Date: 2020/4/22 10:55
* @Description:
*/
@ControllerAdvice
public class GlobalResponseHandle implements ResponseBodyAdvice {
? ? /**
? ? * 處理響應的具體方法
? ? * @param returnType
? ? * @param converterType
? ? * @return
? ? */
? ? @Override
? ? public boolean supports(MethodParameter returnType, Class converterType) {
? ? ? ? return true;
? ? }
? ? @Override
? ? public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
? ? ? ? if (body instanceof ResponseResult) {
? ? ? ? ? ? return body;
? ? ? ? }else{
? ? ? ? ? ? return ResponseResult.success(body);
? ? ? ? }
? ? }
}
```
#### interceptor

###### JwtInterceptor
```
/**
* @author Johnny
* @Date: 2020/4/23 19:41
* @Description:
*/
@Slf4j
@Component
public class JwtInterceptor? implements HandlerInterceptor {
? ? @Resource
? ? SysRoleService roleService;
? ? @Resource
? ? private RedisService redisService;
? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ? String token = request.getHeader("Authorization");
? ? ? ? log.info("token———————————"+token);
? ? ? ? //認證
? ? ? ? if (token == null) {
? ? ? ? ? ? log.info("用戶未登錄");
? ? ? ? ? ? return false;
? ? ? ? }else{
? ? ? ? ? ? //登錄之后
? ? ? ? ? ? //從請求頭當中取出用戶id
? ? ? ? ? ? String adminId = request.getHeader("id");
? ? ? ? ? ? log.info("用戶id" + adminId);
? ? ? ? ? ? //在redis中檢查是否存在adminId為key的數據
? ? ? ? ? ? if (!redisService.existsKey(adminId)) {
? ? ? ? ? ? ? ? log.info("redis中不存在此鍵");
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? String secrect = redisService.getValue(adminId, String.class);
? ? ? ? ? ? ? ? //從token中解析出roles字符串
? ? ? ? ? ? ? ? String roles = JwtTokenUtil.getRoles(token,secrect);
? ? ? ? ? ? ? ? //反序列化成List
? ? ? ? ? ? ? ? List<SysRole> sysRoles = JSONArray.parseArray(roles, SysRole.class);
? ? ? ? ? ? ? ? //從request中取得客戶端傳輸的roleId
? ? ? ? ? ? ? ? String roleId = request.getParameter("roleId");
? ? ? ? ? ? ? ? log.info("roleId:"+roleId);
? ? ? ? ? ? ? ? boolean flag = roleService.checkRole(sysRoles,Integer.parseInt(roleId));
? ? ? ? ? ? ? ? if(flag){
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? log.info("用戶不具備此角色");
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? return false;
? ? ? ? }
? ? }
}
```
###### LoginInterceptor
```
/**
* @author Johnny
* @Date: 2020/4/21 21:00
* @Description:
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
? ? @Resource
? ? private RedisService redisService;
? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle) {
? ? ? ? //將request包裝成HttpServletWrapper類型
? ? ? ? RequestWrapper requestWrapper = new RequestWrapper(request);
? ? ? ? //取得請求的json對象
? ? ? ? String body = requestWrapper.getBody();
? ? ? ? log.info(body);
? ? ? ? //從redis中取得指定用戶名的驗證碼
? ? ? ? JSONObject jsonObject = JSONObject.parseObject(body);
? ? ? ? String name = jsonObject.getString("name");
? ? ? ? System.out.println(name);
? ? ? ? String password = jsonObject.getString("password");
? ? ? ? System.out.println(password);
? ? ? ? String verifyCode = jsonObject.getString("verifyCode");
? ? ? ? System.out.println(verifyCode);
? ? ? ? LoginDto loginDto = LoginDto.builder().name(name).password(password).verifyCode(verifyCode).build();
? ? ? ? return true;
//? ? ? ? if (redisService.existsKey(loginDto.getName())) {
//? ? ? ? ? ? //獲取redis中的驗證碼
//? ? ? ? ? ? String correctCode = redisService.getValue(name, String.class);
//? ? ? ? ? ? //忽略大小寫,成功則放行到controller調用登錄接口
//? ? ? ? ? ? if (correctCode.equalsIgnoreCase(verifyCode)) {
//? ? ? ? ? ? ? ? return true;
//? ? ? ? ? ? }else{
//? ? ? ? ? ? ? ? log.info("驗證碼錯誤");
//? ? ? ? ? ? ? ? return false;
//? ? ? ? ? ? }
//? ? ? ? }else{
//? ? ? ? ? ? log.info("驗證碼失效");
//? ? ? ? ? ? return false;
//? ? ? ? }
? ? }
}
```
#### mapper層
這個目前不做過多贅述,我會盡快整理下mybatisplus的常用語法發出來
#### util層

###### ExcelConsumer
```
/**
* @author Johnny
* @Date: 2020/4/28 21:53
* @Description:
*/
@Slf4j
public class ExcelConsumer<T>? implements Runnable {
? ? /**
? ? * 一張工作表可以容納的最大行數
? ? */
? ? private static Integer SHEET_SIZE = 100000;
? ? /**
? ? * 導出的Vo數據類型
? ? */
? ? private Class<T> clazz;
? ? /**
? ? * 工作簿
? ? */
? ? private SXSSFWorkbook wb;
? ? /**
? ? * 工作表名稱
? ? */
? ? private String sheetName;
? ? /**
? ? * 數據緩沖區對象
? ? */
? ? private ExportDataAdapter<T> exportDataAdapter;
? ? /**
? ? * 線程同步
? ? */
? ? private CountDownLatch latch;
? ? /**
? ? * 構造方法
? ? *
? ? * @param clazz
? ? */
? ? public ExcelConsumer(Class<T> clazz, ExportDataAdapter<T> exportDataAdapter, SXSSFWorkbook wb, CountDownLatch latch, String sheetName) {
? ? ? ? if (clazz == null || wb == null || exportDataAdapter == null || latch == null) {
? ? ? ? ? ? log.error("ExcelConsumer::初始化對象參數不能為空");
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? this.clazz = clazz;
? ? ? ? this.exportDataAdapter = exportDataAdapter;
? ? ? ? this.wb = wb;
? ? ? ? this.latch = latch;
? ? ? ? this.sheetName = sheetName == null ? "UnNamedSheet" : sheetName;
? ? }
? ? @Override
? ? public void run() {
? ? ? ? //初始化excel導出工具類
? ? ? ? ExcelUtil<T> excelUtil = new ExcelUtil<>(this.clazz);
? ? ? ? Sheet sheet = null;
? ? ? ? int sheetNo = 0;
? ? ? ? int rowNum = 1;
? ? ? ? T vo;
? ? ? ? //生產者還在生產數據
? ? ? ? while (latch.getCount() > 1) {
? ? ? ? ? ? //生成sheetName
? ? ? ? ? ? if (rowNum == 1) {
? ? ? ? ? ? ? ? sheetNo++;
? ? ? ? ? ? ? ? sheet = excelUtil.createSheet(wb, sheetName.concat(Integer.toString(sheetNo)));
? ? ? ? ? ? ? ? excelUtil.setColumnTitle(sheet);
? ? ? ? ? ? }
? ? ? ? ? ? //獲取數據
? ? ? ? ? ? vo = exportDataAdapter.getData();
? ? ? ? ? ? //往excel添加一行數據
? ? ? ? ? ? excelUtil.addRowData(vo, wb, sheet, rowNum);
? ? ? ? ? ? rowNum++;
? ? ? ? ? ? //準備生成下一個sheetName
? ? ? ? ? ? if (rowNum == SHEET_SIZE + 1) {
? ? ? ? ? ? ? ? rowNum = 1;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? //生產者不再生產數據,取剩余數據,并將數據寫入excel
? ? ? ? Integer reminderDataSize = exportDataAdapter.getDataSize();
? ? ? ? T reminderData;
? ? ? ? if (reminderDataSize > 0) {
? ? ? ? ? ? for (int i = 0; i < reminderDataSize; i++) {
? ? ? ? ? ? ? ? reminderData = exportDataAdapter.getData();
? ? ? ? ? ? ? ? if (rowNum == 1) {
? ? ? ? ? ? ? ? ? ? sheetNo++;
? ? ? ? ? ? ? ? ? ? sheet = excelUtil.createSheet(wb, sheetName.concat(Integer.toString(sheetNo)));
? ? ? ? ? ? ? ? ? ? excelUtil.setColumnTitle(sheet);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? excelUtil.addRowData(reminderData, wb, sheet, rowNum);
? ? ? ? ? ? ? ? rowNum++;
? ? ? ? ? ? ? ? if (rowNum == SHEET_SIZE + 1) {
? ? ? ? ? ? ? ? ? ? rowNum = 1;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? log.info("數據導出完成");
? ? ? ? latch.countDown();
? ? }
}
```
###### ExcelUtil
```
/**
* @author Johnny
* @Date: 2020/4/28 21:47
* @Description:
*/
public class ExcelUtil<T> {
? ? Class<T> clazz;
? ? /**
? ? * 表頭字段列表
? ? */
? ? private List<Field> fields;
? ? /**
? ? * 數字單元格對象
? ? */
? ? private CellStyle decimalCellStyle = null;
? ? /**
? ? * 日期時間單元格對象
? ? */
? ? private CellStyle dateTimeCellStyle = null;
? ? /**
? ? * 構造方法
? ? *
? ? * @param clazz
? ? */
? ? public ExcelUtil(Class<T> clazz) {
? ? ? ? this.clazz = clazz;
? ? }
? ? /**
? ? * 添加一條數據
? ? *
? ? * @param vo:需要導出的vo對象
? ? * @param wb:工作簿
? ? * @param sheet:工作表
? ? * @param rowNum:當前行號
? ? */
? ? public void addRowData(T vo, SXSSFWorkbook wb, Sheet sheet, int rowNum) {
? ? ? ? //創建一行
? ? ? ? Row row = sheet.createRow(rowNum);
? ? ? ? Field field;
? ? ? ? Cell cell;
? ? ? ? ExcelVoAttribute attr;
? ? ? ? int fieldSize = fields.size();
? ? ? ? // 遍歷入參對象的所有屬性
? ? ? ? for (int j = 0; j < fieldSize; j++) {
? ? ? ? ? ? // 通過反射獲得需要導出的入參對象的所有屬性
? ? ? ? ? ? field = fields.get(j);
? ? ? ? ? ? // 設置實體類私有屬性可訪問
? ? ? ? ? ? field.setAccessible(true);
? ? ? ? ? ? // 獲取所有添加了注解的屬性
? ? ? ? ? ? attr = field.getAnnotation(ExcelVoAttribute.class);
? ? ? ? ? ? // 給每個屬性創建一個單元格
? ? ? ? ? ? cell = row.createCell(j);
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? this.setCellValue(attr, field.get(vo), wb, cell);
? ? ? ? ? ? } catch (IllegalAccessException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? /**
? ? * 根據注解類判斷字段類型設置excel單元格數據格式方法
? ? *
? ? * @param attr
? ? * @param valueObject
? ? * @param workbook
? ? * @param cell
? ? */
? ? private void setCellValue(ExcelVoAttribute attr, Object valueObject, SXSSFWorkbook workbook, Cell cell) {
? ? ? ? String returnValue;
? ? ? ? if (attr.isNumber()) {
? ? ? ? ? ? cell.setCellStyle(getCellStyle(attr, workbook));
? ? ? ? ? ? returnValue = valueObject == null || "".equals(valueObject) ? "0" : valueObject.toString();
? ? ? ? ? ? BigDecimal num = new BigDecimal(returnValue);
? ? ? ? ? ? cell.setCellValue(num.doubleValue());
? ? ? ? } else if (attr.isDateTime()) {
? ? ? ? ? ? cell.setCellStyle(getCellStyle(attr, workbook));
? ? ? ? ? ? DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
? ? ? ? ? ? returnValue = df.format((TemporalAccessor) valueObject);
? ? ? ? ? ? cell.setCellValue(returnValue);
? ? ? ? } else {
? ? ? ? ? ? returnValue = valueObject == null ? "" : valueObject.toString();
? ? ? ? ? ? cell.setCellValue(returnValue);
? ? ? ? }
? ? }
? ? /**
? ? * 根據注解類判斷字段類型,返回excel單元格數據格式方法
? ? *
? ? * @param attr
? ? * @param workbook
? ? * @return CellStyle
? ? */
? ? private CellStyle getCellStyle(ExcelVoAttribute attr, SXSSFWorkbook workbook) {
? ? ? ? if (attr.isNumber()) {
? ? ? ? ? ? if (decimalCellStyle == null) {
? ? ? ? ? ? ? ? decimalCellStyle = workbook.createCellStyle();
? ? ? ? ? ? ? ? //此處設置數字單元格格式
? ? ? ? ? ? ? ? DataFormat df = workbook.createDataFormat();
? ? ? ? ? ? ? ? //千分位,保留1位小數
? ? ? ? ? ? ? ? decimalCellStyle.setDataFormat(df.getFormat("#,##0.0"));
? ? ? ? ? ? }
? ? ? ? ? ? return decimalCellStyle;
? ? ? ? }
? ? ? ? if (attr.isDateTime()) {
? ? ? ? ? ? if (dateTimeCellStyle == null) {
? ? ? ? ? ? ? ? dateTimeCellStyle = workbook.createCellStyle();
? ? ? ? ? ? ? ? //此處設置日期時間單元格格式
? ? ? ? ? ? ? ? DataFormat df = workbook.createDataFormat();
? ? ? ? ? ? ? ? dateTimeCellStyle.setDataFormat(df.getFormat("yyyy-MM-dd HH:mm:ss"));
? ? ? ? ? ? }
? ? ? ? ? ? return dateTimeCellStyle;
? ? ? ? }
? ? ? ? return null;
? ? }
? ? /**
? ? * 創建工作頁Sheet
? ? *
? ? * @param wb
? ? * @param sheetName
? ? * @return Sheet
? ? */
? ? public Sheet createSheet(SXSSFWorkbook wb, String sheetName) {
? ? ? ? return wb.createSheet(sheetName);
? ? }
? ? /**
? ? * 設置excel列頭及格式
? ? *
? ? * @param sheet
? ? */
? ? public void setColumnTitle(Sheet sheet) {
? ? ? ? if (fields == null) {
? ? ? ? ? ? this.fields = this.getSortFields();
? ? ? ? }
? ? ? ? Row row;
? ? ? ? Cell cell;
? ? ? ? ExcelVoAttribute attr;
? ? ? ? Field field;
? ? ? ? int fieldSize = fields.size();
? ? ? ? row = sheet.createRow(0);
? ? ? ? for (int i = 0; i < fieldSize; i++) {
? ? ? ? ? ? field = fields.get(i);
? ? ? ? ? ? attr = field.getAnnotation(ExcelVoAttribute.class);
? ? ? ? ? ? cell = CellUtil.createCell(row, i, attr.name());
? ? ? ? ? ? // 設置列寬,根據相應的字段名的長度等比
? ? ? ? ? ? sheet.setColumnWidth(i, attr.name().getBytes().length * 400);
? ? ? ? }
? ? }
? ? /**
? ? * 獲取輸出對象字段列表,并根據注解進行字段排序
? ? *
? ? * @return
? ? */
? ? private List<Field> getSortFields() {
? ? ? ? List<Field> fields = Arrays.stream(clazz.getDeclaredFields()).filter(x -> x.isAnnotationPresent(ExcelVoAttribute.class)).collect(Collectors.toList());
? ? ? ? List<Field> sortList = new ArrayList<>(fields);
? ? ? ? //排序
? ? ? ? for (Field field : fields) {
? ? ? ? ? ? ExcelVoAttribute excelVoAttribute = field.getAnnotation(ExcelVoAttribute.class);
? ? ? ? ? ? int sortNo = excelVoAttribute.column();
? ? ? ? ? ? sortList.set(sortNo, field);
? ? ? ? }
? ? ? ? return sortList;
? ? }
}
```
###### ExcelUtils
```
/**
* @author Johnny
* @Date: 2020/4/25 22:56
* @Description:
*/
@Slf4j
public class ExcelUtils {
? ? /**
? ? * 導出歌曲
? ? * @param response
? ? * @param list
? ? * @param map
? ? * @param title
? ? */
? ? public static void exportExcel(HttpServletResponse response, List list, Map<String, String> map, String title) {
? ? ? ? //通過工具類創建writer
? ? ? ? ExcelWriter writer = ExcelUtil.getWriter(true);
? ? ? ? //自定義標題別名
? ? ? ? Set<Map.Entry<String, String>> entries = map.entrySet();
? ? ? ? //迭代器遍歷數據
? ? ? ? Iterator<Map.Entry<String, String>> iterator = entries.iterator();
? ? ? ? while (iterator.hasNext()) {
? ? ? ? ? ? Map.Entry<String, String> next = iterator.next();
? ? ? ? ? ? //自定義表頭
? ? ? ? ? ? writer.addHeaderAlias(next.getKey(), next.getValue());
? ? ? ? }
? ? ? ? //合并單元格后的標題行,使用默認標題樣式
? ? ? ? writer.merge(map.size() - 1, title);
? ? ? ? //一次性寫出內容,使用默認樣式,強制輸出標題
? ? ? ? writer.write(list, true);
? ? ? ? //out為outputStream,需要寫出到的目標流
? ? ? ? try {
? ? ? ? ? ? writer.flush(response.getOutputStream(), true);
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? log.info("歌單導出異常");
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? writer.close();
? ? }
? ? /**
? ? * 導入歌曲
? ? * @param file
? ? * @return
? ? */
? ? public static List<Song> importExcel(File file) {
? ? ? ? List<Song> songs = new ArrayList<>();
? ? ? ? InputStream inputStream = null;
? ? ? ? try {
? ? ? ? ? ? inputStream = new FileInputStream(file);
? ? ? ? } catch (FileNotFoundException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? //通過getReader方法確定讀取那個sheet中的數據
? ? ? ? ExcelReader reader = ExcelUtil.getReader(inputStream, "sheet1");
? ? ? ? //回去總行數
? ? ? ? List<List<Object>> read = reader.read(1, reader.getRowCount());
? ? ? ? for (List<Object> objects : read) {
? ? ? ? ? ? //對每行數據取出構建一個song對象
? ? ? ? ? ? Song song = Song.builder()
? ? ? ? ? ? ? ? ? ? .songName(objects.get(0).toString())
? ? ? ? ? ? ? ? ? ? .songId(UUID.randomUUID().toString().replace("-", ""))
? ? ? ? ? ? ? ? ? ? .sortId("0")
? ? ? ? ? ? ? ? ? ? .singer(objects.get(1).toString())
? ? ? ? ? ? ? ? ? ? .duration(objects.get(2).toString())
? ? ? ? ? ? ? ? ? ? .thumbnail(objects.get(3).toString())
? ? ? ? ? ? ? ? ? ? .url(objects.get(4).toString())
? ? ? ? ? ? ? ? ? ? .lyric(objects.get(5).toString())
? ? ? ? ? ? ? ? ? ? .commentCount(0)
? ? ? ? ? ? ? ? ? ? .playCount(0)
? ? ? ? ? ? ? ? ? ? .deleteFlag("0")
? ? ? ? ? ? ? ? ? ? .updateTime(LocalDateTime.now())
? ? ? ? ? ? ? ? ? ? .createTime(LocalDateTime.now())
? ? ? ? ? ? ? ? ? ? .build();
? ? ? ? ? ? songs.add(song);
? ? ? ? }
? ? ? ? return songs;
? ? }
}
```
###### ExportDataAdapter
```
/**
* @author Johnny
* @Date: 2020/4/28 21:53
* @Description:
*/
public class ExportDataAdapter<T>{
? ? /**
? ? * 默認隊列大小
? ? */
? ? private static Integer DEFAULT_SIZE = 1000;
? ? private BlockingQueue<T> resourceQueue = null;
? ? public ExportDataAdapter() {
? ? ? ? this.resourceQueue = new LinkedBlockingQueue<T>(DEFAULT_SIZE);
? ? }
? ? /**
? ? * 添加數據
? ? *
? ? * @param data
? ? */
? ? public void addData(T data) {
? ? ? ? try {
? ? ? ? ? ? resourceQueue.put(data);
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
? ? /**
? ? * 獲取剩余數據數量
? ? *
? ? * @return
? ? */
? ? public Integer getDataSize() {
? ? ? ? return resourceQueue.size();
? ? }
? ? /**
? ? * 從隊列中獲取數據
? ? *
? ? * @return
? ? */
? ? public T getData() {
? ? ? ? try {
? ? ? ? ? ? return resourceQueue.take();
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return null;
? ? }
}
```
###### FileUtil
```
/**
* @author Johnny
* @Date: 2020/4/26 10:41
* @Description:
*/
public class FileUtil {
? ? public static File fileConversion(MultipartFile file) {
? ? ? ? int n;
? ? ? ? File file1 = new File(file.getOriginalFilename());
? ? ? ? try {
? ? ? ? ? ? InputStream in = file.getInputStream();
? ? ? ? ? ? OutputStream os = new FileOutputStream(file1);
? ? ? ? ? ? byte[] bytes = new byte[4096];
? ? ? ? ? ? while ((n=in.read(bytes,0,4096))!=-1){
? ? ? ? ? ? ? ? os.write(bytes,0,n);
? ? ? ? ? ? ? ? File file2 = new File(file1.toURI());
? ? ? ? ? ? ? ? file2.delete();
? ? ? ? ? ? }
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return file1;
? ? }
}
```
###### JwtTokenUtil
```
/**
* @Author Johnny
* @Date 2020/4/15
* @Version 1.0
*/
@Slf4j
public class JwtTokenUtil {
? ? /**
? ? * 加密
? ? *
? ? * @param userId
? ? * @param expiresAt
? ? * @return String
? ? */
? ? public static String getToken(final String userId, String userRole,final String secrect,Date expiresAt) {
? ? ? ? String token = null;
? ? ? ? try {
? ? ? ? ? ? token = JWT.create()
? ? ? ? ? ? ? ? ? ? .withIssuer("auth0")
? ? ? ? ? ? ? ? ? ? .withClaim("userId", userId)
? ? ? ? ? ? ? ? ? ? .withClaim("userRole", userRole)
? ? ? ? ? ? ? ? ? ? .withExpiresAt(expiresAt)
? ? ? ? ? ? ? ? ? ? // 使用了HMAC256加密算法, mySecret是用來加密數字簽名的密鑰
? ? ? ? ? ? ? ? ? ? .sign(Algorithm.HMAC256(secrect));
? ? ? ? } catch (UnsupportedEncodingException e) {
? ? ? ? ? ? log.error("不支持的編碼格式");
? ? ? ? }
? ? ? ? return token;
? ? }
? ? /**
? ? * 解密
? ? * @param token
? ? * @return DecodedJWT
? ? */
? ? public static DecodedJWT deToken(final String token,final String secrect) {
? ? ? ? DecodedJWT jwt;
? ? ? ? JWTVerifier verifier = null;
? ? ? ? try {
? ? ? ? ? ? verifier = JWT.require(Algorithm.HMAC256(secrect))
? ? ? ? ? ? ? ? ? ? .withIssuer("auth0")
? ? ? ? ? ? ? ? ? ? .build();
? ? ? ? } catch (UnsupportedEncodingException e) {
? ? ? ? ? ? log.error("不支持的編碼格式");
? ? ? ? }
? ? ? ? assert verifier != null;
? ? ? ? jwt = verifier.verify(token);
? ? ? ? return jwt;
? ? }
? ? /**
? ? * 獲取userId
? ? *
? ? * @param token
? ? * @return String
? ? */
? ? public static String getUserId(String token,String secrect) {
? ? ? ? return deToken(token,secrect).getClaim("userId").asString();
? ? }
? ? /**
? ? * 獲取role
? ? *
? ? * @param token
? ? * @return String
? ? */
? ? public static String getRoles(String token,String secrect) {
? ? ? ? return deToken(token,secrect).getClaim("userRole").asString();
? ? }
? ? /**
? ? * 驗證是否過期
? ? *
? ? * @param token
? ? * @return boolean
? ? */
? ? public static boolean isExpiration(String token,String secrect) {
? ? ? ? return deToken(token,secrect).getExpiresAt().before(new Date());
? ? }
? ? public static void main(String[] args) {
//? ? ? ? String token = getToken("2000100193", new Date(System.currentTimeMillis() + 10L * 1000L));
//? ? ? ? System.out.println(token);
//? ? ? ? while (true) {
//? ? ? ? ? ? boolean flag = isExpiration(token);
//? ? ? ? ? ? System.out.println(flag);
//? ? ? ? ? ? if (flag) {
//? ? ? ? ? ? ? ? System.out.println("token已失效");
//? ? ? ? ? ? ? ? break;
//? ? ? ? ? ? }
//? ? ? ? ? ? try {
//? ? ? ? ? ? ? ? Thread.sleep(1000);
//? ? ? ? ? ? } catch (InterruptedException e) {
//? ? ? ? ? ? ? ? e.printStackTrace();
//? ? ? ? ? ? }
//? ? ? ? }
//? ? ? ? String token = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCIsImV4cCI6MTU4NzY3MTQ3OCwidXNlcklkIjoiREUzNUQ3Q0MwNUFGOTZBMjFEN0FERkM4NjUxRTY2MTQifQ.uT2W3QcE3744WNN3inEKT8lUVJs6xAC7TodDCaWkcyM";
//? ? ? ? System.out.println(deToken(token).getClaim("userId").asString());
? ? }
}
```
###### Md5Util
```
/**
* @author Johnny
*/
public class Md5Util {
? ? /**
? ? * @param pwd? ? 需要加密的字符串
? ? * @param isUpper 字母大小寫(false為默認小寫,true為大寫)
? ? * @param bit? ? 加密的位數(16,32,64)
? ? * @return String
? ? */
? ? public static String getMd5(String pwd, boolean isUpper, Integer bit) {
? ? ? ? String md5 = "";
? ? ? ? try {
? ? ? ? ? ? // 創建加密對象
? ? ? ? ? ? MessageDigest md = MessageDigest.getInstance("md5");
? ? ? ? ? ? if (bit == 64) {
? ? ? ? ? ? ? ? Base64.Encoder encoder = Base64.getEncoder();
? ? ? ? ? ? ? ? md5 = encoder.encodeToString(md.digest(pwd.getBytes(StandardCharsets.UTF_8)));
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? // 計算MD5函數
? ? ? ? ? ? ? ? md.update(pwd.getBytes());
? ? ? ? ? ? ? ? byte b[] = md.digest();
? ? ? ? ? ? ? ? int i;
? ? ? ? ? ? ? ? StringBuilder sb = new StringBuilder();
? ? ? ? ? ? ? ? for (byte value : b) {
? ? ? ? ? ? ? ? ? ? i = value;
? ? ? ? ? ? ? ? ? ? if (i < 0) {
? ? ? ? ? ? ? ? ? ? ? ? i += 256;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (i < 16) {
? ? ? ? ? ? ? ? ? ? ? ? sb.append("0");
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? sb.append(Integer.toHexString(i));
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? md5 = sb.toString();
? ? ? ? ? ? ? ? if (bit == 16) {
? ? ? ? ? ? ? ? ? ? //截取32位md5為16位
? ? ? ? ? ? ? ? ? ? md5 = md5.substring(8, 24).toString();
? ? ? ? ? ? ? ? ? ? if (isUpper) {
? ? ? ? ? ? ? ? ? ? ? ? md5 = md5.toUpperCase();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? return md5;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? //轉換成大寫
? ? ? ? ? ? if (isUpper) {
? ? ? ? ? ? ? ? md5 = md5.toUpperCase();
? ? ? ? ? ? }
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? System.out.println("md5加密拋出異常!");
? ? ? ? }
? ? ? ? return md5;
? ? }
? ? public static void main(String[] args) {
? ? ? ? String a = "123456";
? ? ? ? String md5a = getMd5(a, true, 32);
? ? ? ? System.out.println(md5a);
? ? ? ? System.out.println(md5a.length());
? ? }
```
###### ThreadPool
```
/**
* @author Johnny
* @Date: 2020/4/28 21:55
* @Description:
*/
public class ThreadPool {
? ? /**
? ? * 異步線程
? ? */
? ? private final static ThreadPoolExecutor executor =
? ? ? ? ? ? new ThreadPoolExecutor(20, 100, 10, TimeUnit.MINUTES,
? ? ? ? ? ? ? ? ? ? new ArrayBlockingQueue<>(2000),
? ? ? ? ? ? ? ? ? ? r -> new Thread(r, "excelExportThread"),
? ? ? ? ? ? ? ? ? ? new ThreadPoolExecutor.AbortPolicy());
? ? public static ThreadPoolExecutor getExecutor() {
? ? ? ? return executor;
? ? }
}
```
###### TreeBuilder
```
/**
* @author Johnny
* @Date: 2020/4/23 18:46
* @Description:
*/
public class TreeBuilder {
? ? /**
? ? * 兩層循環實現建樹
? ? *
? ? * @param treeNodes 傳入的樹節點列表
? ? * @return
? ? */
? ? public static List<TreeNode> buildTreeByLoop(List<TreeNode> treeNodes) {
? ? ? ? List<TreeNode> trees = new ArrayList<>();
? ? ? ? for (TreeNode treeNode : treeNodes) {
? ? ? ? ? ? if (treeNode.getParentId() == 0) {
? ? ? ? ? ? ? ? trees.add(treeNode);
? ? ? ? ? ? }
? ? ? ? ? ? for (TreeNode it : treeNodes) {
? ? ? ? ? ? ? ? if (it.getParentId().equals(treeNode.getId())) {
? ? ? ? ? ? ? ? ? ? if (treeNode.getSubMenus() == null) {
? ? ? ? ? ? ? ? ? ? ? ? treeNode.setSubMenus(new ArrayList<>());
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? treeNode.getSubMenus().add(it);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return trees;
? ? }
? ? /**
? ? * 使用遞歸方法建樹
? ? *
? ? * @param treeNodes
? ? * @return
? ? */
? ? public static List<TreeNode> buildTreeByRecursive(List<TreeNode> treeNodes) {
? ? ? ? List<TreeNode> trees = new ArrayList<>();
? ? ? ? for (TreeNode treeNode : treeNodes) {
? ? ? ? ? ? if (treeNode.getParentId() == 0) {
? ? ? ? ? ? ? ? trees.add(findChildren(treeNode, treeNodes));
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return trees;
? ? }
? ? /**
? ? * 遞歸查找子節點
? ? *
? ? * @param treeNodes
? ? * @return
? ? */
? ? public static TreeNode findChildren(TreeNode treeNode, List<TreeNode> treeNodes) {
? ? ? ? for (TreeNode it : treeNodes) {
? ? ? ? ? ? if (treeNode.getId().equals(it.getParentId())) {
? ? ? ? ? ? ? ? if (treeNode.getSubMenus() == null) {
? ? ? ? ? ? ? ? ? ? treeNode.setSubMenus(new ArrayList<>());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? treeNode.getSubMenus().add(findChildren(it, treeNodes));
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return treeNode;
? ? }
}
```
###### TreeNode
```
/**
* @author Johnny
* @Date: 2020/4/23 18:46
* @Description:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TreeNode {
? ? private Integer id;
? ? private Integer parentId;
? ? private Integer type;
? ? private String title;
? ? private String icon;
? ? private String path;
? ? private Integer sort;
? ? private List<TreeNode> subMenus;
? ? public TreeNode(Integer id, Integer parentId, Integer type,String title, String icon, String path, Integer sort) {
? ? ? ? this.id = id;
? ? ? ? this.parentId = parentId;
? ? ? ? this.type = type;
? ? ? ? this.title = title;
? ? ? ? this.icon = icon;
? ? ? ? this.path = path;
? ? ? ? this.sort = sort;
? ? }
}
```、
更多內容請關注Johnny博客