主要用於寫@ExcelVoAttribute註解,能夠更靈活的配置Excel數據表導出的一些基本配置前端
主要用於寫@LogDirection註解,註解裏包含一個字段(接口描述),經過接口添加這個註解,填寫上接口的功能,當用戶在訪問接口時候,能夠利用aop技術,把用戶的操做記到日誌裏java
主要定義了aop切點的位置和加強的方法mysql
/** * @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的方法web
@Around定義加強的方法,經過around的參數ProceedingJoinPoint能夠很好的控制程序能不能繼續執行下一步,在權限斷定發揮着比較重要的做用redis
拓展:算法
利用下面的這種方法,須要在xml文件中配置bean實例,固然也能夠在方法中經過手動掃包的方法得到bean實例進行操做spring
/** * @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配置實例sql
<bean name="forumDao" class="com.soft1851.spring.web.dao.impl.ForumDaoImpl"/> <bean name="myAdvice" class="com.soft1851.spring.web.interceptor.MyAdvice"/> <aop:aspectj-autoproxy/>
ResponseResult和ResultCode數據庫
這兩個類能夠將響應體統一進行封裝,將接口的響應體規範化,使前端獲取數據也更加友好json
貼下代碼,防止之後找不到
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(); } }
主要是爲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 |
主要是爲跨域作一些基本的配置,學習階段後端必備
代碼展現,防止之後忘記
/** * @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; } }
主要是爲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; } }
代碼生成器,功能強大無比,根據數據表,一鍵生成各類層級,各類實體
/** * @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緩存數據庫進行一些基本配置
/** * @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; } }
這個類中主要配置了攔截器,能夠對部分頁面進行統一攔截,固然這個只是統一的攔截,具體的攔截所要進行的操做,還在下文
/** * @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層經常使用註解彙總整理下
目前尚未將entity、vo、dto統一放到domain裏,dto裏主要是用戶請求接口時須要傳的參數,vo主要時後端返回前端展現的參數,而entity就是咱們和數據庫表所吻合的類。舉例:咱們數據庫中的用戶表的字段確定不僅時帳號和密碼,會有一些其餘字段,好比加密鹽、用戶基本信息等等,可是用戶請求登陸接口時,只須要傳帳號和密碼就能夠了,此時此刻,爲了防止資源的浪費,咱們就定義一個dto來接收前端傳來的參數。當用戶進入主界面時,查看我的信息,後端並不須要將用戶id、加密鹽等私密信息展現到前端,此時,一樣爲了節省資源,咱們定義vo視圖對象,將前端須要的參數放進去,再傳入前端。
entity層主要是將數據庫中的表一對一的寫好,此處是直接用代碼生成器生成
定義全局異常處理,使用時直接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; } }
也是全局異常處理,但和上面略有區別,非手動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); } }
同CustomException類格式同樣,但這個側重於處理Jwt模塊的異常
/** * @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() { } }
這個類主要針對用戶登陸進行過濾
/** * @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() { } }
定義全局異常
/** * @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); }
處理全局的響應頭(當接口返回類型非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); } } }
/** * @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; } } }
/** * @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; // } } }
這個目前不作過多贅述,我會盡快整理下mybatisplus的經常使用語法發出來
/** * @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(); } }
/** * @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; } }
/** * @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; } }
/** * @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; } }
/** * @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; } }
/** * @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()); } }
/** * @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()); }
/** * @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; } }
/** * @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; } }
/** * @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博客