├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── xyz │ │ └── snwjas │ │ └── blog │ │ ├── Application.java │ │ ├── annotation │ │ ├── AccessLimit.java │ │ ├── ActionRecord.java │ │ └── TimeCost.java │ │ ├── aspect │ │ ├── ActionRecordAspect.java │ │ └── TimeCostAspect.java │ │ ├── config │ │ ├── AppStartConfig.java │ │ ├── HibernateValidationConfig.java │ │ ├── LocalDateTimeSerializerConfig.java │ │ ├── MyBatisPlusConfig.java │ │ ├── MyBlogConfig.java │ │ ├── SwaggerConfig.java │ │ ├── WebMvcConfig.java │ │ ├── WebSecurityConfig.java │ │ └── properties │ │ │ └── MyBlogProperties.java │ │ ├── constant │ │ ├── CacheKeyPrefix.java │ │ ├── MyBlogOptionEnum.java │ │ ├── OptionEnum.java │ │ ├── OtherOptionEnum.java │ │ └── RS.java │ │ ├── controller │ │ ├── MainController.java │ │ ├── admin │ │ │ ├── AttachmentController.java │ │ │ ├── BlogController.java │ │ │ ├── CategoryController.java │ │ │ ├── CommentController.java │ │ │ ├── LinkController.java │ │ │ ├── LogController.java │ │ │ ├── OptionsController.java │ │ │ ├── SpeciallistController.java │ │ │ ├── StatisticsController.java │ │ │ ├── TagController.java │ │ │ └── UserController.java │ │ └── app │ │ │ ├── BlogController.java │ │ │ ├── CategoryController.java │ │ │ ├── CommentController.java │ │ │ ├── IndexController.java │ │ │ ├── LinkController.java │ │ │ └── TagController.java │ │ ├── exception │ │ ├── MyBlogException.java │ │ └── ServiceException.java │ │ ├── handler │ │ └── RestUnifiedExceptionHandler.java │ │ ├── interceptor │ │ ├── AccessLimitInterceptor.java │ │ └── StatisticInterceptor.java │ │ ├── mapper │ │ ├── AttachmentMapper.java │ │ ├── BlogMapper.java │ │ ├── BlogTagMapper.java │ │ ├── CategoryMapper.java │ │ ├── CommentMapper.java │ │ ├── LinkMapper.java │ │ ├── LogMapper.java │ │ ├── OptionsMapper.java │ │ ├── SpecaillistMapper.java │ │ ├── StatisticsMapper.java │ │ ├── TagMapper.java │ │ ├── UserMapper.java │ │ └── xml │ │ │ ├── AttachmentMapper.xml │ │ │ ├── BlogMapper.xml │ │ │ ├── BlogTagMapper.xml │ │ │ ├── CategoryMapper.xml │ │ │ ├── CommentMapper.xml │ │ │ ├── LinkMapper.xml │ │ │ ├── LogMapper.xml │ │ │ ├── OptionsMapper.xml │ │ │ ├── SpeciallistMapper.xml │ │ │ ├── StatisticsMapper.xml │ │ │ ├── TagMapper.xml │ │ │ └── UserMapper.xml │ │ ├── model │ │ ├── PageResult.java │ │ ├── R.java │ │ ├── UserDetail.java │ │ ├── base │ │ │ ├── BeanConvert.java │ │ │ └── ValidGroupType.java │ │ ├── entity │ │ │ ├── AttachmentEntity.java │ │ │ ├── BaseEntity.java │ │ │ ├── BlogEntity.java │ │ │ ├── BlogTagEntity.java │ │ │ ├── CategoryEntity.java │ │ │ ├── CommentEntity.java │ │ │ ├── LinkEntity.java │ │ │ ├── LogEntity.java │ │ │ ├── OptionsEntity.java │ │ │ ├── SpeciallistEntity.java │ │ │ ├── StatisticsEntity.java │ │ │ ├── TagEntity.java │ │ │ └── UserEntity.java │ │ ├── enums │ │ │ ├── BlogCommentAllowStatus.java │ │ │ ├── BlogStatus.java │ │ │ ├── CommentStatus.java │ │ │ ├── LogType.java │ │ │ ├── SpecialListType.java │ │ │ └── ValueEnum.java │ │ ├── params │ │ │ ├── AttachmentSearchParam.java │ │ │ ├── BasePageParam.java │ │ │ ├── BlogSearchParam.java │ │ │ ├── CommentSearchParam.java │ │ │ ├── LinkSearchParam.java │ │ │ ├── ListParam.java │ │ │ ├── LogSearchParam.java │ │ │ ├── SpeciallistSearchParam.java │ │ │ └── UpdatePasswordParam.java │ │ └── vo │ │ │ ├── AttachmentVO.java │ │ │ ├── BlogArchiveVO.java │ │ │ ├── BlogDetailVO.java │ │ │ ├── BlogSelectVO.java │ │ │ ├── BlogSimpleVO.java │ │ │ ├── CategoryVO.java │ │ │ ├── CommentDetailVO.java │ │ │ ├── CommentSimpleVO.java │ │ │ ├── LinkVO.java │ │ │ ├── LogVO.java │ │ │ ├── OptionVO.java │ │ │ ├── SpeciallistVO.java │ │ │ ├── StatisticsBasicVO.java │ │ │ ├── StatisticsReportVo.java │ │ │ ├── TagVO.java │ │ │ └── UserVO.java │ │ ├── schedule │ │ └── StatisticTask.java │ │ ├── service │ │ ├── AttachmentService.java │ │ ├── BlogService.java │ │ ├── CategoryService.java │ │ ├── CommentService.java │ │ ├── LinkService.java │ │ ├── LogService.java │ │ ├── OptionsService.java │ │ ├── SpeciallistService.java │ │ ├── StatisticsService.java │ │ ├── TagService.java │ │ ├── UserService.java │ │ └── impl │ │ │ ├── AttachmentServiceImpl.java │ │ │ ├── BlogServiceImpl.java │ │ │ ├── CategoryServiceImpl.java │ │ │ ├── CommentServiceImpl.java │ │ │ ├── LinkServiceImpl.java │ │ │ ├── LogServiceImpl.java │ │ │ ├── OptionsServiceImpl.java │ │ │ ├── SpeciallistServiceImpl.java │ │ │ ├── StatisticsServiceImpl.java │ │ │ ├── TagServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ ├── support │ │ ├── cache │ │ │ ├── CacheStore.java │ │ │ ├── CacheWrapper.java │ │ │ └── MemoryCacheStore.java │ │ ├── security │ │ │ ├── MyAuthenticationFailureHandler.java │ │ │ ├── MyAuthenticationSuccessHandler.java │ │ │ ├── MyLogoutSuccessHandler.java │ │ │ └── MyUserDetailsService.java │ │ └── wordfilter │ │ │ ├── EndType.java │ │ │ ├── FlagIndex.java │ │ │ ├── WordContext.java │ │ │ ├── WordFilter.java │ │ │ └── WordType.java │ │ └── utils │ │ ├── ClassUtils.java │ │ ├── CodeAutoGenerator.java │ │ ├── DateUtils.java │ │ ├── FileUtils.java │ │ ├── IPUtils.java │ │ ├── JsonUtils.java │ │ ├── LambdaTypeUtils.java │ │ ├── RUtils.java │ │ ├── RWriterUtils.java │ │ ├── StrUtils.java │ │ └── URLUtils.java └── resources │ ├── admin │ └── .gitkeep │ ├── app │ └── .gitkeep │ ├── application-dev.yaml │ ├── application-prod.yaml │ ├── application.yaml │ ├── banner.txt │ ├── schema.sql │ └── static │ └── .gitkeep └── test └── java └── xyz └── snwjas └── blog └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.iml 4 | src/main/resources/admin/* 5 | !src/main/resources/admin/.gitkeep 6 | src/main/resources/app/* 7 | !src/main/resources/app/.gitkeep 8 | src/main/resources/static/* 9 | !src/main/resources/static/.gitkeep 10 | src/test/java/xyz/snwjas/blog/* 11 | !src/test/java/xyz/snwjas/blog/.gitkeep 12 | src/main/resources/BugRecords.md 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Myles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/Application.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | import org.springframework.scheduling.annotation.EnableScheduling; 7 | 8 | @EnableScheduling 9 | @ConfigurationPropertiesScan("xyz.snwjas.blog.config.properties") 10 | @SpringBootApplication 11 | public class Application { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(Application.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/annotation/AccessLimit.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 接口防刷限流(固定时间内最大访问次数) 10 | * 11 | * @author Myles Yang 12 | */ 13 | @Target({ElementType.METHOD}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface AccessLimit { 16 | 17 | /** 18 | * 最大访问次数 19 | */ 20 | int maxCount() default Integer.MAX_VALUE; 21 | 22 | /** 23 | * 固定时间 24 | */ 25 | int seconds() default 1; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/annotation/ActionRecord.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.annotation; 2 | 3 | import org.springframework.core.annotation.AliasFor; 4 | import xyz.snwjas.blog.aspect.ActionRecordAspect; 5 | import xyz.snwjas.blog.model.enums.LogType; 6 | 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | /** 13 | * 操作记录 14 | * 15 | * @author Myles Yang 16 | */ 17 | @Target({ElementType.METHOD}) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | public @interface ActionRecord { 20 | 21 | /** 22 | * 操作内容的EL表达式,仅支持方法参数 23 | */ 24 | @AliasFor("content") 25 | String value() default ""; 26 | 27 | /** 28 | * 操作内容,与 value 互为别名 29 | */ 30 | @AliasFor("value") 31 | String content() default ""; 32 | 33 | /** 34 | * 操作类型 35 | */ 36 | LogType type() default LogType.COMMON; 37 | 38 | /** 39 | * 生效条件的EL表达式

40 | * 它是通过返回值判断,返回值符号为{@link ActionRecordAspect#METHOD_RETURNING_SIGN}

41 | * 示例(函数返回值为0):

42 | * EL表达式:"#ret > 0",结果 false 43 | */ 44 | String condition() default ""; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/annotation/TimeCost.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 方法执行计时 10 | */ 11 | @Target(ElementType.METHOD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface TimeCost { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/aspect/ActionRecordAspect.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.aspect; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.JoinPoint; 5 | import org.aspectj.lang.annotation.AfterReturning; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.aspectj.lang.annotation.Pointcut; 8 | import org.aspectj.lang.reflect.MethodSignature; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.expression.EvaluationContext; 11 | import org.springframework.expression.ExpressionParser; 12 | import org.springframework.expression.spel.standard.SpelExpressionParser; 13 | import org.springframework.expression.spel.support.StandardEvaluationContext; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.util.StringUtils; 16 | import xyz.snwjas.blog.annotation.ActionRecord; 17 | import xyz.snwjas.blog.model.vo.LogVO; 18 | import xyz.snwjas.blog.service.LogService; 19 | import xyz.snwjas.blog.utils.IPUtils; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import java.lang.reflect.Method; 23 | import java.util.Objects; 24 | 25 | /** 26 | * 操作记录注解切面 27 | * 28 | * @author Myles Yang 29 | */ 30 | @Component 31 | @Aspect 32 | @Slf4j 33 | public class ActionRecordAspect { 34 | 35 | /** 36 | * 方法返回值符号 37 | */ 38 | public static final String METHOD_RETURNING_SIGN = "ret"; 39 | 40 | /** 41 | * el 表达式 解析器 42 | */ 43 | private static final ExpressionParser SPEL_PARSER = new SpelExpressionParser(); 44 | 45 | @Autowired 46 | private LogService logService; 47 | 48 | 49 | @Pointcut("@annotation(xyz.snwjas.blog.annotation.ActionRecord)") 50 | public void pointCut() { 51 | } 52 | 53 | /** 54 | * 方法正常返回的advice 55 | * 56 | * @param point 方法的连接点 57 | * @param ret 函数返回值,void的返回值为null 58 | */ 59 | @AfterReturning(value = "pointCut()", returning = METHOD_RETURNING_SIGN, argNames = "point,ret") 60 | public void afterReturning(JoinPoint point, Object ret) { 61 | 62 | MethodSignature signature = (MethodSignature) point.getSignature(); 63 | Method method = signature.getMethod(); 64 | ActionRecord annotation = method.getAnnotation(ActionRecord.class); 65 | // 条件判断是否执行日志记录 66 | if (StringUtils.hasText(annotation.condition())) { 67 | try { 68 | Boolean condition = (Boolean) spell(annotation.condition(), 69 | new String[]{METHOD_RETURNING_SIGN}, new Object[]{ret}); 70 | if (!(Objects.nonNull(condition) && condition)) { 71 | return; 72 | } 73 | } catch (Exception e) { 74 | log.error("条件EL表达式解析错误:{}", annotation.condition()); 75 | e.printStackTrace(); 76 | return; 77 | } 78 | } 79 | 80 | LogVO logVO = new LogVO(); 81 | 82 | // 测试时 @AliasFor 失效,原因未知 83 | String content = StringUtils.isEmpty(annotation.value()) 84 | ? annotation.content() 85 | : annotation.value(); 86 | 87 | if (StringUtils.hasText(content)) { 88 | try { 89 | String cont = (String) spell(content, 90 | signature.getParameterNames(), point.getArgs()); 91 | logVO.setContent(cont); 92 | } catch (Exception e) { 93 | log.error("内容EL表达式解析错误:{}", content); 94 | e.printStackTrace(); 95 | return; 96 | } 97 | } 98 | 99 | logVO.setType(annotation.type()); 100 | 101 | HttpServletRequest request = IPUtils.getRequest(); 102 | if (Objects.nonNull(request)) { 103 | String ipAddress = IPUtils.getIpAddress(request); 104 | logVO.setIpAddress(ipAddress); 105 | } 106 | 107 | logService.add(logVO); 108 | } 109 | 110 | /** 111 | * el表达式解析 112 | * 113 | * @param el 表达式 114 | * @param names 参数名称数组 115 | * @param args 参数数组 116 | */ 117 | public Object spell(String el, String[] names, Object[] args) { 118 | EvaluationContext context = new StandardEvaluationContext(); 119 | for (int i = 0; i < args.length; i++) { 120 | context.setVariable(names[i], args[i]); 121 | } 122 | return SPEL_PARSER.parseExpression(el).getValue(context); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/aspect/TimeCostAspect.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.aspect; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.JoinPoint; 5 | import org.aspectj.lang.annotation.AfterReturning; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.aspectj.lang.annotation.Before; 8 | import org.aspectj.lang.annotation.Pointcut; 9 | import org.aspectj.lang.reflect.MethodSignature; 10 | import org.springframework.core.annotation.Order; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.lang.reflect.Method; 14 | import java.time.Instant; 15 | import java.time.temporal.ChronoUnit; 16 | import java.util.Objects; 17 | 18 | /** 19 | * 方法执行计时注解切面 20 | */ 21 | @Component 22 | @Aspect 23 | @Slf4j 24 | @Order(1) 25 | public class TimeCostAspect { 26 | 27 | private final ThreadLocal instantThreadLocal = new ThreadLocal<>(); 28 | 29 | 30 | @Pointcut("@annotation(xyz.snwjas.blog.annotation.TimeCost)") 31 | public void pointCut() { 32 | } 33 | 34 | @Before("pointCut()") 35 | public void before() { 36 | instantThreadLocal.set(Instant.now()); 37 | } 38 | 39 | @AfterReturning("pointCut()") 40 | public void afterReturning(JoinPoint point) { 41 | if (Objects.nonNull(instantThreadLocal.get())) { 42 | Instant now = Instant.now(); 43 | MethodSignature signature = (MethodSignature) point.getSignature(); 44 | Method method = signature.getMethod(); 45 | // String methodParameterTypes = StringUtils.join(method.getGenericParameterTypes(), ", "); 46 | log.info("类:{},方法:{},执行耗时:{}毫秒", 47 | method.getDeclaringClass().getName(), 48 | method.getName() /*+ "(" + methodParameterTypes + ")"*/, 49 | ChronoUnit.MILLIS.between(instantThreadLocal.get(), now)); 50 | instantThreadLocal.remove(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/AppStartConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.context.event.ApplicationReadyEvent; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.event.EventListener; 8 | import org.springframework.util.StringUtils; 9 | import xyz.snwjas.blog.constant.MyBlogOptionEnum; 10 | import xyz.snwjas.blog.constant.OptionEnum; 11 | import xyz.snwjas.blog.model.enums.SpecialListType; 12 | import xyz.snwjas.blog.model.vo.OptionVO; 13 | import xyz.snwjas.blog.model.vo.SpeciallistVO; 14 | import xyz.snwjas.blog.service.OptionsService; 15 | import xyz.snwjas.blog.service.SpeciallistService; 16 | import xyz.snwjas.blog.support.wordfilter.WordFilter; 17 | import xyz.snwjas.blog.support.wordfilter.WordType; 18 | import xyz.snwjas.blog.utils.ClassUtils; 19 | 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Objects; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | 25 | /** 26 | * 应用启动后的配置 27 | * 28 | * @author Myles Yang 29 | */ 30 | @Configuration 31 | @Slf4j 32 | public class AppStartConfig { 33 | 34 | @Autowired 35 | private OptionsService optionsService; 36 | 37 | @Autowired 38 | private SpeciallistService speciallistService; 39 | 40 | @Autowired 41 | private WordFilter wordFilter; 42 | 43 | 44 | /** 45 | * 博客初始化 46 | */ 47 | @EventListener(ApplicationReadyEvent.class) 48 | public void initialization() { 49 | OptionVO birthday = optionsService.get(MyBlogOptionEnum.BIRTHDAY.key()); 50 | // 第一次启动应用 51 | if (Objects.isNull(birthday)) { 52 | for (Class clazz : ClassUtils.getAllClassByInterface(OptionEnum.class)) { 53 | for (OptionEnum oenum : clazz.getEnumConstants()) { 54 | if (StringUtils.hasText(oenum.key())) { 55 | String value = oenum.defaultValue() == null ? "" : oenum.defaultValue(); 56 | optionsService.setIfAbsence(oenum.key(), value); 57 | } 58 | } 59 | } 60 | log.info("首次启动应用,已初始化设置"); 61 | } 62 | } 63 | 64 | /** 65 | * 初始化敏感词过滤器词条 66 | */ 67 | @EventListener(ApplicationReadyEvent.class) 68 | public void initWordFilter() throws Exception { 69 | Object PRESENT = new Object(); 70 | Map blackList = new ConcurrentHashMap<>(128); 71 | Map whiteList = new ConcurrentHashMap<>(32); 72 | List list = speciallistService.listAll(SpecialListType.WORD_BLACK_LIST, SpecialListType.WORD_WHITE_LIST); 73 | list.stream().parallel().forEach(o -> { 74 | if (Objects.nonNull(o.getType())) { 75 | if (SpecialListType.WORD_BLACK_LIST.equals(o.getType())) { 76 | blackList.put(o.getContent(), PRESENT); 77 | } else if (SpecialListType.WORD_WHITE_LIST.equals(o.getType())) { 78 | whiteList.put(o.getContent(), PRESENT); 79 | } 80 | } 81 | }); 82 | wordFilter.getContext().addWord(blackList.keySet(), WordType.BLACK); 83 | wordFilter.getContext().addWord(whiteList.keySet(), WordType.WHITE); 84 | log.info("加载敏感词词条完毕:黑名单 {} 条,白名单 {} 条", blackList.size(), whiteList.size()); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/HibernateValidationConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config; 2 | 3 | import org.hibernate.validator.HibernateValidator; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; 7 | 8 | import javax.validation.Validation; 9 | import javax.validation.Validator; 10 | import javax.validation.ValidatorFactory; 11 | 12 | /** 13 | * Spring Validation 配置 14 | * 15 | * @author Myles Yang 16 | */ 17 | @Configuration 18 | public class HibernateValidationConfig { 19 | 20 | /** 21 | * 开启Fail Fast模式,一旦校验失败就立即返回 22 | * Spring Validation 默认会校验完所有字段,然后才抛出异常。 23 | */ 24 | @Bean 25 | public Validator validator() { 26 | ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) 27 | .configure() 28 | .failFast(true) 29 | .buildValidatorFactory(); 30 | return validatorFactory.getValidator(); 31 | } 32 | 33 | /** 34 | * 方法参数检验,注意在类上添加 @Validated 注解 35 | */ 36 | @Bean 37 | public MethodValidationPostProcessor methodValidationPostProcessor() { 38 | MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor(); 39 | // 设置validator模式为快速失败返回 40 | postProcessor.setValidator(validator()); 41 | return postProcessor; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/LocalDateTimeSerializerConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.SerializationFeature; 6 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 7 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 8 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 9 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; 10 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 11 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 12 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.context.annotation.Primary; 16 | 17 | import java.text.SimpleDateFormat; 18 | import java.time.LocalDate; 19 | import java.time.LocalDateTime; 20 | import java.time.LocalTime; 21 | import java.time.format.DateTimeFormatter; 22 | import java.util.List; 23 | 24 | /** 25 | * 请求、响应的日期格式全局格式化 26 | */ 27 | @Configuration 28 | public class LocalDateTimeSerializerConfig { 29 | 30 | public static final String DEFAULT_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; 31 | 32 | public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd"; 33 | 34 | public static final String DEFAULT_TIME_PATTERN = "HH:mm:ss"; 35 | 36 | /** 37 | * 自定义 LocalDateTime、LocalDate、LocalTime 的序列化 与 反序列化 38 | * 1.配置 MappingJackson2HttpMessageConverter{@link WebMvcConfig#customJackson2HttpMessageConverter()} 39 | * 2.重写 WebMVC 的 configureMessageConverters{@link WebMvcConfig#configureMessageConverters(List)} 40 | */ 41 | @Bean 42 | @Primary 43 | public ObjectMapper objectMapper() { 44 | 45 | ObjectMapper om = new ObjectMapper(); 46 | om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 47 | om.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); 48 | 49 | JavaTimeModule module = new JavaTimeModule(); 50 | module 51 | .addSerializer(LocalDateTime.class, 52 | new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN))) 53 | .addSerializer(LocalDate.class, 54 | new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN))) 55 | .addSerializer(LocalTime.class, 56 | new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN))) 57 | .addDeserializer(LocalDateTime.class, 58 | new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN))) 59 | .addDeserializer(LocalDate.class, 60 | new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN))) 61 | .addDeserializer(LocalTime.class, 62 | new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN))); 63 | 64 | om.registerModule(module); 65 | 66 | om.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_PATTERN)); 67 | 68 | return om; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/MyBatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 6 | import org.mybatis.spring.annotation.MapperScan; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.transaction.annotation.EnableTransactionManagement; 10 | 11 | @Configuration 12 | @EnableTransactionManagement 13 | @MapperScan("xyz.snwjas.blog.mapper") 14 | public class MyBatisPlusConfig { 15 | 16 | /** 17 | * MybatisPlus 拦截器 18 | */ 19 | @Bean 20 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 21 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 22 | 23 | // 注册分页插件 24 | PaginationInnerInterceptor pii = new PaginationInnerInterceptor(DbType.MYSQL); 25 | // 最大单页限制数量 26 | pii.setMaxLimit(100L); 27 | 28 | interceptor.addInnerInterceptor(pii); 29 | return interceptor; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/MyBlogConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 7 | import xyz.snwjas.blog.support.cache.MemoryCacheStore; 8 | import xyz.snwjas.blog.support.wordfilter.WordContext; 9 | import xyz.snwjas.blog.support.wordfilter.WordFilter; 10 | 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | 13 | /** 14 | * 博客配置 15 | * 16 | * @author Myles Yang 17 | */ 18 | @Configuration 19 | @Slf4j 20 | public class MyBlogConfig { 21 | 22 | /** 23 | * 自定义缓存 24 | */ 25 | @Bean 26 | public MemoryCacheStore memoryCacheStore() { 27 | return new MemoryCacheStore(); 28 | } 29 | 30 | /** 31 | * 敏感词过滤器 32 | */ 33 | @Bean 34 | public WordFilter wordFilter() { 35 | return new WordFilter(new WordContext()); 36 | } 37 | 38 | /** 39 | * 线程池 40 | */ 41 | @Bean("executor") 42 | public ThreadPoolTaskExecutor threadPoolTaskExecutor() { 43 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 44 | executor.setCorePoolSize(Runtime.getRuntime().availableProcessors()); 45 | executor.setMaxPoolSize(128); 46 | executor.setQueueCapacity(1024); 47 | executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); 48 | executor.setWaitForTasksToCompleteOnShutdown(true); 49 | executor.setAwaitTerminationSeconds(3); 50 | return executor; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.service.Contact; 11 | import springfox.documentation.spi.DocumentationType; 12 | import springfox.documentation.spring.web.plugins.Docket; 13 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 14 | import xyz.snwjas.blog.config.properties.MyBlogProperties; 15 | 16 | import java.time.temporal.Temporal; 17 | 18 | /** 19 | * Swagger 配置 20 | * 21 | * @author Myles Yang 22 | */ 23 | @Configuration 24 | @EnableSwagger2 25 | @Slf4j 26 | public class SwaggerConfig { 27 | 28 | private final MyBlogProperties properties; 29 | 30 | public SwaggerConfig(MyBlogProperties properties) { 31 | this.properties = properties; 32 | } 33 | 34 | @Bean 35 | public Docket docket() { 36 | if (!properties.isDocEnable()) { 37 | log.debug("Swagger Doc has been disabled."); 38 | } 39 | return buildDocket("Default", 40 | "xyz.snwjas.blog.controller", 41 | "/**") 42 | .enable(properties.isDocEnable()); 43 | } 44 | 45 | private Docket buildDocket(String groupName, String basePackage, String antPattern) { 46 | return new Docket(DocumentationType.SWAGGER_2) 47 | .groupName(groupName) 48 | .select() 49 | .apis(RequestHandlerSelectors.basePackage(basePackage)) 50 | .paths(PathSelectors.ant(antPattern)) 51 | .build() 52 | .apiInfo(apiInfo()) 53 | .directModelSubstitute(Temporal.class, String.class); 54 | 55 | } 56 | 57 | private ApiInfo apiInfo() { 58 | return new ApiInfoBuilder() 59 | .title("MyBlog API Documentation.") 60 | .description("The API documentation for MyBlog.") 61 | .version("1.0.0") 62 | .contact(new Contact("Myles Yang", "snwjas.xyz", "myles.yang@foxmail.com")) 63 | .license("MIT") 64 | .licenseUrl("") 65 | .build(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | import org.springframework.security.web.csrf.CsrfFilter; 12 | import org.springframework.web.filter.CharacterEncodingFilter; 13 | import xyz.snwjas.blog.config.properties.MyBlogProperties; 14 | import xyz.snwjas.blog.support.security.MyAuthenticationFailureHandler; 15 | import xyz.snwjas.blog.support.security.MyAuthenticationSuccessHandler; 16 | import xyz.snwjas.blog.support.security.MyLogoutSuccessHandler; 17 | import xyz.snwjas.blog.support.security.MyUserDetailsService; 18 | 19 | /** 20 | * Spring Security 配置 21 | * 22 | * @author Myles Yang 23 | */ 24 | @EnableWebSecurity 25 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 26 | 27 | @Autowired 28 | private MyBlogProperties properties; 29 | 30 | @Autowired 31 | private MyUserDetailsService myUserDetailsService; 32 | 33 | @Autowired 34 | private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; 35 | 36 | @Autowired 37 | private MyAuthenticationFailureHandler myAuthenticationFailureHandler; 38 | 39 | @Autowired 40 | private MyLogoutSuccessHandler myLogoutSuccessHandler; 41 | 42 | /** 43 | * 密码加密器 44 | */ 45 | @Bean 46 | public PasswordEncoder passwordEncoder() { 47 | // the version of bcrypt, can be 2a,2b,2y 48 | // the log rounds to use, between 4 and 31 49 | return new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2Y, 5); 50 | } 51 | 52 | /** 53 | * 自定义登录业务逻辑配置 54 | */ 55 | @Override 56 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 57 | auth.userDetailsService(myUserDetailsService); 58 | } 59 | 60 | /** 61 | * Http 安全配置 62 | */ 63 | @Override 64 | protected void configure(HttpSecurity http) throws Exception { 65 | 66 | // 允许来自同一域的任何请求不被添加到响应中 67 | http.headers().frameOptions().sameOrigin().httpStrictTransportSecurity().disable(); 68 | 69 | // 关闭csrf,允许跨域访问 70 | http.csrf().disable().cors(); 71 | 72 | // 放行 与 拦截 的请求 73 | http.authorizeRequests() 74 | .antMatchers("/api/admin/user/login", "/api/admin/user/checkLogin").permitAll() 75 | .antMatchers("/api/admin/**").authenticated(); 76 | 77 | // 登录表单验证处理 78 | http.formLogin() 79 | .loginPage("/api/admin/user/login") 80 | .usernameParameter("username") 81 | .passwordParameter("password") 82 | // 认证成功处理 83 | .successHandler(myAuthenticationSuccessHandler) 84 | // 认证失败处理 85 | .failureHandler(myAuthenticationFailureHandler); 86 | 87 | // 注销 88 | http.logout() 89 | .logoutUrl("/api/admin/user/logout") 90 | .deleteCookies("JSESSIONID", "rememberMe") 91 | .invalidateHttpSession(true) 92 | // 注销成功处理 93 | .logoutSuccessHandler(myLogoutSuccessHandler); 94 | 95 | // 开启登录的记住我功能,SpringSecurity中token默认有效期为两周 96 | // checkbox 支持校验的值["true", "on", "yes", "1"] 97 | // 具体参考 {@link AbstractRememberMeServices#rememberMeRequested(HttpServletRequest, String)} 98 | http.rememberMe() 99 | .rememberMeParameter("rememberMe") 100 | .tokenValiditySeconds(properties.getRememberMeTokenValiditySeconds()); 101 | 102 | // 编码过滤器 103 | CharacterEncodingFilter enf = new CharacterEncodingFilter("UTF-8", false, true); 104 | http.addFilterBefore(enf, CsrfFilter.class); 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/config/properties/MyBlogProperties.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.config.properties; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | import java.nio.file.Paths; 8 | 9 | /** 10 | * 博客配置 11 | * 12 | * @author Myles Yang 13 | */ 14 | @Getter 15 | @Setter 16 | @ConfigurationProperties(prefix = "my-blog") 17 | public class MyBlogProperties { 18 | /** 19 | * swagger doc 20 | */ 21 | private boolean docEnable = false; 22 | 23 | /** 24 | * 后台管理入口,不需要添加'/' 25 | */ 26 | private String adminPath = "admin"; 27 | 28 | /** 29 | * 自定义管理端WEB静态文件所在目录,默认为“classpath:/admin/” 30 | */ 31 | private String adminWebPath = null; 32 | 33 | /** 34 | * 自定义展示前端WEB静态文件所在目录,默认为“classpath:/app/” 35 | */ 36 | private String appWebPath = null; 37 | 38 | /** 39 | * 允许连续登录失败的时间(单位秒) 40 | */ 41 | private int allowLoginFailureSeconds = 3600; 42 | 43 | /** 44 | * 允许登录失败的次数 45 | */ 46 | private int allowLoginFailureCount = 10; 47 | 48 | 49 | /** 50 | * 登录记住我token时间(单位秒,1周 = 604800秒) 51 | */ 52 | private int rememberMeTokenValiditySeconds = 604800; 53 | 54 | /** 55 | * 上传文件保存路径 56 | */ 57 | private String fileSavePath = Paths.get(System.getProperty("user.home"), "MyBlog", "files").toString(); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/constant/CacheKeyPrefix.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.constant; 2 | 3 | /** 4 | * Redis key 前缀 常量 5 | * 6 | * @author Myles Yang 7 | */ 8 | public interface CacheKeyPrefix { 9 | 10 | /** 11 | * 分隔符 12 | */ 13 | String SEPARATOR = ":"; 14 | 15 | /** 16 | * 认证失败次数前缀 17 | */ 18 | String LOGIN_FAILED_COUNT = "loginFailedCount:"; 19 | 20 | /** 21 | * 访问限制次数(接口限流) 22 | */ 23 | String ACCESS_LIMIT_PREFIX = "accessLimit:"; 24 | 25 | /** 26 | * 网站访问量 27 | */ 28 | String WEB_VISIT_COUNT = "webVisitCount:"; 29 | 30 | /** 31 | * 博客文章访问量 32 | */ 33 | String BLOG_VISIT_COUNT = "blogVisitCount:"; 34 | 35 | /** 36 | * 博客点赞 37 | */ 38 | String BLOG_LIKE = "blogLike:"; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/constant/MyBlogOptionEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.constant; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | /** 6 | * MyBlog Option Enum 7 | * 8 | * @author Myles Yang 9 | */ 10 | public enum MyBlogOptionEnum implements OptionEnum { 11 | 12 | /** 13 | * 博客主页url 14 | */ 15 | URL("url", String.class, "http://"), 16 | 17 | /** 18 | * 博客名 19 | */ 20 | NAME("name", String.class, "MyBlog"), 21 | 22 | /** 23 | * 博客描述 24 | */ 25 | DESCRIPTION("description", String.class, "MyBlog"), 26 | 27 | /** 28 | * 博客logo 29 | */ 30 | LOGO("logo", String.class, ""), 31 | 32 | /** 33 | * 博客favicon 34 | */ 35 | FAVICON("favicon", String.class, ""), 36 | 37 | /** 38 | * 博客页脚信息 39 | */ 40 | FOOTER("footer", String.class, "Copyright © 2020 MyBlog"), 41 | 42 | /** 43 | * 博客建立日期 44 | */ 45 | BIRTHDAY("birthday", String.class, LocalDateTime.now().toString()), 46 | 47 | 48 | ; 49 | 50 | private final String key; 51 | 52 | private final Class type; 53 | 54 | private final String defaultValue; 55 | 56 | MyBlogOptionEnum(String key, Class type, String defaultValue) { 57 | this.key = key; 58 | this.type = type; 59 | this.defaultValue = defaultValue; 60 | } 61 | 62 | @Override 63 | public String key() { 64 | return key; 65 | } 66 | 67 | @Override 68 | public Class type() { 69 | return type; 70 | } 71 | 72 | @Override 73 | public String defaultValue() { 74 | return defaultValue; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/constant/OptionEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.constant; 2 | 3 | /** 4 | * 博客设置枚举接口 5 | * 6 | * @author Myles Yang 7 | */ 8 | public interface OptionEnum { 9 | 10 | /** 11 | * 设置项的唯一标识 12 | */ 13 | String key(); 14 | 15 | /** 16 | * 设置项的类型,用于解析时进行类型转换 17 | */ 18 | Class type(); 19 | 20 | /** 21 | * 设置项默认值 22 | */ 23 | String defaultValue(); 24 | 25 | 26 | /** 27 | * 获得optionValue的原类型值 28 | */ 29 | @SuppressWarnings("unchecked") 30 | static T getTrueOptionValue(String value, Class type) { 31 | if (type.isAssignableFrom(String.class)) { 32 | return (T) value; 33 | } 34 | 35 | if (type.isAssignableFrom(Integer.class)) { 36 | return (T) Integer.valueOf(value); 37 | } 38 | 39 | if (type.isAssignableFrom(Long.class)) { 40 | return (T) Long.valueOf(value); 41 | } 42 | 43 | if (type.isAssignableFrom(Boolean.class)) { 44 | return (T) Boolean.valueOf(value); 45 | } 46 | 47 | if (type.isAssignableFrom(Short.class)) { 48 | return (T) Short.valueOf(value); 49 | } 50 | 51 | if (type.isAssignableFrom(Byte.class)) { 52 | return (T) Byte.valueOf(value); 53 | } 54 | 55 | if (type.isAssignableFrom(Double.class)) { 56 | return (T) Double.valueOf(value); 57 | } 58 | 59 | if (type.isAssignableFrom(Float.class)) { 60 | return (T) Float.valueOf(value); 61 | } 62 | 63 | return (T) value; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/constant/OtherOptionEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.constant; 2 | 3 | /** 4 | * todo 5 | * 6 | * @author Myles Yang 7 | */ 8 | public enum OtherOptionEnum implements OptionEnum { 9 | 10 | URL_FAVICON_PARSER("url_favicon_parser", String.class, ""), 11 | 12 | ; 13 | 14 | private final String key; 15 | 16 | private final Class type; 17 | 18 | private final String defaultValue; 19 | 20 | OtherOptionEnum(String key, Class type, String defaultValue) { 21 | this.key = key; 22 | this.type = type; 23 | this.defaultValue = defaultValue; 24 | } 25 | 26 | @Override 27 | public String key() { 28 | return key; 29 | } 30 | 31 | @Override 32 | public Class type() { 33 | return type; 34 | } 35 | 36 | @Override 37 | public String defaultValue() { 38 | return defaultValue; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/constant/RS.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.constant; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | /** 6 | * 自定义状态码(Response Status) 7 | *

8 | * 200 成功 9 | * 1001 - 1999 参数 10 | * 2001 - 2999 用户 11 | * 3001 - 3999 权限 12 | * 4001 - 4999 数据 13 | * 5001 - 5999 业务 14 | * 6001 - 6999 接口 15 | * 7001 - 7999 系统 16 | * 17 | * @author Myles Yang 18 | */ 19 | public enum RS { 20 | 21 | SUCCESS(HttpStatus.OK.value(), "成功"), 22 | 23 | /** 24 | * 参数错误 25 | */ 26 | ILLEGAL_PARAMETER(1001, "非法参数"), 27 | 28 | FILE_SIZE_LIMIT(1002, "上传文件过大"), 29 | 30 | /** 31 | * 用户错误 32 | */ 33 | USERNAME_PASSWORD_ERROR(2001, "用户名或密码错误"), 34 | 35 | USERNAME_ERROR(2002, "用户名错误"), 36 | 37 | PASSWORD_ERROR(2003, "密码错误"), 38 | 39 | INCONSISTENT_PASSWORDS(2004, "两次输入的密码不一致"), 40 | 41 | MULTIPLE_AUTHENTICATION_FAILURE(2005, "多次认证失败"), 42 | 43 | /** 44 | * 权限 45 | */ 46 | NOT_LOGIN(3001, "用户未登录"), 47 | 48 | 49 | /** 50 | * 接口错误 51 | */ 52 | FREQUENT_OPERATION(6001, "操作过于频繁,请稍后再试"), 53 | 54 | METHOD_NOT_SUPPORTED(6002, "请求方法有误"), 55 | 56 | PAGE_NOT_FOUND(6003, "请求目标不存在"), 57 | 58 | /** 59 | * 系统错误 60 | */ 61 | SYSTEM_ERROR(7001, "系统错误"), 62 | 63 | ; 64 | 65 | private final int status; 66 | 67 | private final String message; 68 | 69 | RS(int status, String message) { 70 | this.status = status; 71 | this.message = message; 72 | } 73 | 74 | public int status() { 75 | return status; 76 | } 77 | 78 | public String message() { 79 | return message; 80 | } 81 | 82 | public static RS resolve(int statusCode) { 83 | for (RS rc : values()) { 84 | if (rc.status == statusCode) { 85 | return rc; 86 | } 87 | } 88 | return null; 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return "{\"status\":" + status + ",\"message\":\"" + message + "\"}"; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/MainController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.util.StringUtils; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import xyz.snwjas.blog.annotation.AccessLimit; 10 | import xyz.snwjas.blog.config.properties.MyBlogProperties; 11 | import xyz.snwjas.blog.constant.MyBlogOptionEnum; 12 | import xyz.snwjas.blog.model.vo.OptionVO; 13 | import xyz.snwjas.blog.service.OptionsService; 14 | import xyz.snwjas.blog.model.R; 15 | import xyz.snwjas.blog.utils.RUtils; 16 | import xyz.snwjas.blog.utils.URLUtils; 17 | 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.io.IOException; 20 | import java.util.Map; 21 | import java.util.Objects; 22 | 23 | /** 24 | * Main Controller 25 | * 26 | * @author Myles Yang 27 | */ 28 | @RestController 29 | @Api(value = "主控制器", tags = {"主接口"}) 30 | public class MainController { 31 | 32 | @Autowired 33 | private MyBlogProperties properties; 34 | 35 | @Autowired 36 | private OptionsService optionsService; 37 | 38 | /** 39 | * 后台入口 40 | */ 41 | @GetMapping("${my-blog.admin-path:admin}") 42 | @ApiOperation("后台入口") 43 | public void toAdmin(HttpServletResponse response) throws IOException { 44 | response.sendRedirect("/" + properties.getAdminPath() + "/index.html"); 45 | } 46 | 47 | /** 48 | * 前台入口 49 | */ 50 | @GetMapping({"/", "/index"}) 51 | @ApiOperation("前台入口") 52 | public void index(HttpServletResponse response) throws IOException { 53 | response.sendRedirect("/app/index.html"); 54 | } 55 | 56 | @AccessLimit(maxCount = 2) 57 | @GetMapping("/favicon.ico") 58 | @ApiOperation("获取Favicon图标") 59 | public void getFavicon(HttpServletResponse response) throws IOException { 60 | OptionVO vo = optionsService.get(MyBlogOptionEnum.FAVICON.key()); 61 | if (Objects.nonNull(vo) && StringUtils.hasText(vo.getOptionValue())) { 62 | response.sendRedirect(URLUtils.encodeAll(vo.getOptionValue())); 63 | } 64 | } 65 | 66 | @AccessLimit(maxCount = 2) 67 | @GetMapping("/attributes") 68 | @ApiOperation("获取博客的基本属性") 69 | public R getAttributes() { 70 | Map attributes = optionsService.listNecessary(); 71 | return RUtils.success("基本属性", attributes); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/BlogController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | import xyz.snwjas.blog.model.base.ValidGroupType; 10 | import xyz.snwjas.blog.model.entity.BlogEntity; 11 | import xyz.snwjas.blog.model.params.BlogSearchParam; 12 | import xyz.snwjas.blog.model.vo.BlogDetailVO; 13 | import xyz.snwjas.blog.model.vo.BlogSelectVO; 14 | import xyz.snwjas.blog.model.vo.BlogSimpleVO; 15 | import xyz.snwjas.blog.service.BlogService; 16 | import xyz.snwjas.blog.model.PageResult; 17 | import xyz.snwjas.blog.model.R; 18 | import xyz.snwjas.blog.utils.RUtils; 19 | 20 | import javax.validation.Valid; 21 | import javax.validation.constraints.Min; 22 | import java.util.List; 23 | import java.util.Objects; 24 | 25 | /** 26 | * Article Controller 27 | * 28 | * @author Myles Yang 29 | */ 30 | @Validated 31 | @RestController("AdminBlogController") 32 | @RequestMapping("/api/admin/blog") 33 | @Api(value = "后台博客文章控制器", tags = {"后台博客文章接口"}) 34 | public class BlogController { 35 | 36 | @Autowired 37 | private BlogService blogService; 38 | 39 | @PostMapping("/list") 40 | @ApiOperation("获取博客文章列表信息") 41 | public R listBlogs(@RequestBody @Valid BlogSearchParam param) { 42 | IPage blogEntityIPage = blogService.pageBy(param); 43 | PageResult pageResult = blogService.covertToPageResult(blogEntityIPage); 44 | return RUtils.success("博客文章列表信息", pageResult); 45 | } 46 | 47 | @GetMapping("/list/all-titles") 48 | @ApiOperation("获取所有的文章标题") 49 | public R listAllTitle() { 50 | List list = blogService.listAllTitle(); 51 | return RUtils.success("所有的文章标题", list); 52 | } 53 | 54 | @GetMapping("/get/{id}") 55 | @ApiOperation("获取博客文章信息") 56 | public R getBlog(@PathVariable("id") @Min(1) Integer blogId) { 57 | BlogDetailVO detailVO = blogService.getDetailById(blogId); 58 | if (Objects.isNull(detailVO)) { 59 | return RUtils.fail("无此博客文章信息"); 60 | } 61 | return RUtils.success("博客文章信息", detailVO); 62 | } 63 | 64 | @PostMapping("/add") 65 | @ApiOperation("发表博客文章") 66 | public R addBlog(@RequestBody @Validated(ValidGroupType.Save.class) BlogDetailVO vo) { 67 | int i = blogService.add(vo); 68 | return RUtils.commonFailOrNot(i, "发表博客文章"); 69 | } 70 | 71 | @PostMapping("/update") 72 | @ApiOperation("更新博客文章信息") 73 | public R updateBlog(@RequestBody @Validated(ValidGroupType.Update.class) BlogDetailVO vo) { 74 | if (blogService.isExist(vo.getId(), vo.getUrl())) { 75 | return RUtils.fail("博客路径已存在"); 76 | } 77 | int i = blogService.update(vo); 78 | return RUtils.commonFailOrNot(i, "博客信息文章更新"); 79 | } 80 | 81 | @DeleteMapping("/delete/{blogId}") 82 | @ApiOperation("删除博客文章") 83 | public R deleteBlog(@PathVariable("blogId") @Min(1) Integer blogId) { 84 | int i = blogService.deleteById(blogId); 85 | return RUtils.commonFailOrNot(i, "删除博客文章"); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/CategoryController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.swagger.annotations.Api; 6 | import io.swagger.annotations.ApiOperation; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.validation.annotation.Validated; 9 | import org.springframework.web.bind.annotation.*; 10 | import xyz.snwjas.blog.model.PageResult; 11 | import xyz.snwjas.blog.model.R; 12 | import xyz.snwjas.blog.model.base.ValidGroupType; 13 | import xyz.snwjas.blog.model.entity.CategoryEntity; 14 | import xyz.snwjas.blog.model.params.BasePageParam; 15 | import xyz.snwjas.blog.model.vo.CategoryVO; 16 | import xyz.snwjas.blog.service.BlogService; 17 | import xyz.snwjas.blog.service.CategoryService; 18 | import xyz.snwjas.blog.utils.RUtils; 19 | 20 | import javax.validation.Valid; 21 | import javax.validation.constraints.Min; 22 | import javax.validation.constraints.NotNull; 23 | import java.util.List; 24 | 25 | /** 26 | * Category Controller 27 | * 28 | * @author Myles Yang 29 | */ 30 | @Validated 31 | @RestController("AdminCategoryController") 32 | @RequestMapping("/api/admin/blog/category") 33 | @Api(value = "后台分类控制器", tags = {"后台分类接口"}) 34 | public class CategoryController { 35 | 36 | @Autowired 37 | private CategoryService categoryService; 38 | 39 | @Autowired 40 | private BlogService blogService; 41 | 42 | @PostMapping("/list") 43 | @ApiOperation("分页获取博客分类") 44 | public R list(@RequestBody @Valid BasePageParam param) { 45 | IPage categoryPage = categoryService.pageBy(param); 46 | PageResult pageResult = categoryService.covertToPageResult(categoryPage); 47 | // 获取每个分类下的文章数目 48 | for (CategoryVO vo : pageResult.getList()) { 49 | int count = blogService.getCountByCategoryId(vo.getId(), null); 50 | vo.setBlogCount(count); 51 | } 52 | return RUtils.success("博客分类分页信息", pageResult); 53 | } 54 | 55 | @GetMapping("/list/all") 56 | @ApiOperation("获取所有博客分类") 57 | public R listAll() { 58 | List categoryVOList = categoryService.listAllCategory(); 59 | return RUtils.success("所有的博客分类", categoryVOList); 60 | } 61 | 62 | @GetMapping("/list/used") 63 | @ApiOperation("获取已使用的博客分类") 64 | public R listUsed() { 65 | List usedCategory = categoryService.listUsedCategory(); 66 | return RUtils.success("已使用的博客分类", usedCategory); 67 | } 68 | 69 | @DeleteMapping("/delete/{categoryId}") 70 | @ApiOperation("删除分类") 71 | public R delete(@PathVariable("categoryId") @NotNull @Min(1) Integer categoryId) { 72 | int i = categoryService.deleteById(categoryId); 73 | return RUtils.commonFailOrNot(i, "删除分类"); 74 | } 75 | 76 | @PostMapping("/add") 77 | @ApiOperation("添加分类") 78 | public R add(@RequestBody @Validated({ValidGroupType.Save.class}) CategoryVO vo) { 79 | int i = categoryService.add(vo); 80 | if (i > 0) { 81 | return RUtils.success("添加分类成功"); 82 | } 83 | if (i < 0) { 84 | return RUtils.fail("分类名已存在"); 85 | } 86 | return RUtils.fail("添加分类失败"); 87 | } 88 | 89 | @PostMapping("/update") 90 | @ApiOperation("更新分类") 91 | public R update(@RequestBody @Validated({ValidGroupType.Update.class}) CategoryVO vo) { 92 | int i = categoryService.update(vo); 93 | return RUtils.commonFailOrNot(i, "更新分类"); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/CommentController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.swagger.annotations.Api; 6 | import io.swagger.annotations.ApiOperation; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.validation.annotation.Validated; 9 | import org.springframework.web.bind.annotation.*; 10 | import xyz.snwjas.blog.model.entity.CommentEntity; 11 | import xyz.snwjas.blog.model.enums.CommentStatus; 12 | import xyz.snwjas.blog.model.params.CommentSearchParam; 13 | import xyz.snwjas.blog.model.params.ListParam; 14 | import xyz.snwjas.blog.model.vo.CommentDetailVO; 15 | import xyz.snwjas.blog.model.vo.CommentSimpleVO; 16 | import xyz.snwjas.blog.service.CommentService; 17 | import xyz.snwjas.blog.model.PageResult; 18 | import xyz.snwjas.blog.model.R; 19 | import xyz.snwjas.blog.utils.RUtils; 20 | 21 | import javax.validation.Valid; 22 | import javax.validation.constraints.Min; 23 | import javax.validation.constraints.NotNull; 24 | 25 | /** 26 | * Comment Controller 27 | * 28 | * @author Myles Yang 29 | */ 30 | @Validated 31 | @RestController("AdminCommentController") 32 | @RequestMapping("/api/admin/comment") 33 | @Api(value = "后台评论控制器", tags = {"后台评论接口"}) 34 | public class CommentController { 35 | 36 | @Autowired 37 | private CommentService commentService; 38 | 39 | @PostMapping("/list") 40 | @ApiOperation("获取评论列表信息") 41 | public R list(@RequestBody @Valid CommentSearchParam param) { 42 | IPage blogEntityIPage = commentService.pageBy(param); 43 | PageResult pageResult = commentService.covertToDetailPageResult(blogEntityIPage); 44 | return RUtils.success("评论列表信息", pageResult); 45 | } 46 | 47 | @PostMapping("/update/status") 48 | @ApiOperation("更新评论状态") 49 | public R updateStatus(@RequestParam("id") @NotNull @Min(1) Integer id, 50 | @RequestParam("status") @NotNull CommentStatus status) { 51 | int i = commentService.changeStatus(id, status); 52 | return RUtils.commonFailOrNot(i, "更新评论状态"); 53 | } 54 | 55 | @PostMapping("/update/status-batch") 56 | @ApiOperation("批量更新评论状态") 57 | public R updateStatusBatch(@RequestParam("ids") @Validated ListParam ids, 58 | @RequestParam("status") @NotNull CommentStatus status) { 59 | int count = commentService.changeStatus(ids, status); 60 | return RUtils.success("成功更新了" + count + "条评论的状态"); 61 | } 62 | 63 | @DeleteMapping("/delete/{commentId}") 64 | @ApiOperation("删除评论") 65 | public R delete(@PathVariable("commentId") @Min(1) Integer commentId) { 66 | int i = commentService.deleteById(commentId); 67 | return RUtils.commonFailOrNot(i, "删除评论"); 68 | } 69 | 70 | @DeleteMapping("/delete") 71 | @ApiOperation("批量删除评论") 72 | public R delete(@RequestBody @Validated ListParam commentIds) { 73 | int i = commentService.deleteByIds(commentIds); 74 | return RUtils.commonFailOrNot(i, "批量删除评论"); 75 | } 76 | 77 | @PostMapping("/reply") 78 | @ApiOperation("回复评论") 79 | public R reply(@RequestBody @Validated(CommentSimpleVO.Admin.class) CommentDetailVO vo) { 80 | int i = commentService.reply(vo); 81 | return RUtils.commonFailOrNot(i, "回复评论"); 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/LinkController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.swagger.annotations.Api; 6 | import io.swagger.annotations.ApiOperation; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.lang.NonNull; 9 | import org.springframework.validation.annotation.Validated; 10 | import org.springframework.web.bind.annotation.*; 11 | import xyz.snwjas.blog.constant.OtherOptionEnum; 12 | import xyz.snwjas.blog.model.base.ValidGroupType; 13 | import xyz.snwjas.blog.model.entity.LinkEntity; 14 | import xyz.snwjas.blog.model.params.LinkSearchParam; 15 | import xyz.snwjas.blog.model.vo.LinkVO; 16 | import xyz.snwjas.blog.service.LinkService; 17 | import xyz.snwjas.blog.model.PageResult; 18 | import xyz.snwjas.blog.model.R; 19 | import xyz.snwjas.blog.service.OptionsService; 20 | import xyz.snwjas.blog.utils.RUtils; 21 | 22 | import javax.validation.Valid; 23 | import javax.validation.constraints.Min; 24 | 25 | /** 26 | * Link Controller 27 | * 28 | * @author Myles Yang 29 | */ 30 | @Validated 31 | @RestController("AdminLinkController") 32 | @RequestMapping("/api/admin/link") 33 | @Api(value = "后台友链控制器", tags = {"后台友链接口"}) 34 | public class LinkController { 35 | 36 | @Autowired 37 | private LinkService linkService; 38 | 39 | @Autowired 40 | private OptionsService optionsService; 41 | 42 | 43 | @PostMapping("/list") 44 | @ApiOperation("获取友链列表信息") 45 | public R list(@RequestBody @Valid LinkSearchParam param) { 46 | IPage page = linkService.pageBy(param); 47 | PageResult pageResult = linkService.covertToPageResult(page); 48 | return RUtils.success("友链列表信息", pageResult); 49 | } 50 | 51 | @PostMapping("/update") 52 | @ApiOperation("更新友链信息") 53 | public R updateBlog(@RequestBody @Validated(ValidGroupType.Update.class) LinkVO vo) { 54 | int i = linkService.update(vo); 55 | return RUtils.commonFailOrNot(i, "友链信息更新"); 56 | } 57 | 58 | @DeleteMapping("/delete/{linkId}") 59 | @ApiOperation("删除友链") 60 | public R deleteBlog(@PathVariable("linkId") @Min(1) Integer linkId) { 61 | int i = linkService.deleteById(linkId); 62 | return RUtils.commonFailOrNot(i, "删除友链"); 63 | } 64 | 65 | @PostMapping("/add") 66 | @ApiOperation("添加友链") 67 | public R addBlog(@RequestBody @Validated(ValidGroupType.Save.class) LinkVO vo) { 68 | int i = linkService.add(vo); 69 | return RUtils.commonFailOrNot(i, "友链添加"); 70 | } 71 | 72 | @PostMapping("/update/parser") 73 | @ApiOperation("更新网站Logo(favicon)解析API") 74 | public R updateUrlLogoParser(@RequestParam("parser") @NonNull String logoParser) { 75 | int i = optionsService.setAnyway(OtherOptionEnum.URL_FAVICON_PARSER.key(), logoParser); 76 | if (i > 0) { 77 | int j = linkService.updateLogoParser(logoParser); 78 | if (j > 0) { 79 | optionsService.resetOptionsCache(); 80 | } 81 | } 82 | return RUtils.commonFailOrNot(i, "友链Logo解析API修改"); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/LogController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | import xyz.snwjas.blog.model.PageResult; 10 | import xyz.snwjas.blog.model.R; 11 | import xyz.snwjas.blog.model.entity.LogEntity; 12 | import xyz.snwjas.blog.model.params.ListParam; 13 | import xyz.snwjas.blog.model.params.LogSearchParam; 14 | import xyz.snwjas.blog.model.vo.LogVO; 15 | import xyz.snwjas.blog.service.LogService; 16 | import xyz.snwjas.blog.utils.RUtils; 17 | 18 | import javax.validation.Valid; 19 | 20 | /** 21 | * Log Controller 22 | * 23 | * @author Myles Yang 24 | */ 25 | @Validated 26 | @RestController("AdminLogController") 27 | @RequestMapping("/api/admin/log") 28 | @Api(value = "后台日志控制器", tags = {"后台日志接口"}) 29 | public class LogController { 30 | 31 | @Autowired 32 | private LogService logService; 33 | 34 | 35 | @PostMapping("/list") 36 | @ApiOperation("获取日志列表信息") 37 | public R listBlogs(@RequestBody @Valid LogSearchParam param) { 38 | IPage page = logService.pageBy(param); 39 | PageResult pageResult = logService.covertToPageResult(page); 40 | return RUtils.success("日志列表信息", pageResult); 41 | } 42 | 43 | @DeleteMapping("/delete") 44 | @ApiOperation("批量删除评论") 45 | public R delete(@RequestBody @Validated ListParam logIds) { 46 | int i = logService.deleteByIds(logIds); 47 | return RUtils.commonFailOrNot(i, "批量删除日志"); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/OptionsController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import lombok.extern.slf4j.Slf4j; 7 | import net.coobird.thumbnailator.Thumbnails; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.scheduling.annotation.Async; 10 | import org.springframework.util.StringUtils; 11 | import org.springframework.web.bind.annotation.*; 12 | import xyz.snwjas.blog.config.properties.MyBlogProperties; 13 | import xyz.snwjas.blog.service.OptionsService; 14 | import xyz.snwjas.blog.model.R; 15 | import xyz.snwjas.blog.utils.RUtils; 16 | import xyz.snwjas.blog.utils.URLUtils; 17 | 18 | import java.io.BufferedInputStream; 19 | import java.io.File; 20 | import java.net.URL; 21 | import java.net.URLConnection; 22 | import java.util.Map; 23 | import java.util.Objects; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | /** 27 | * Option Controller 28 | * 29 | * @author Myles Yang 30 | */ 31 | @Slf4j 32 | @RestController("AdminOptionsController") 33 | @RequestMapping("/api/admin/option") 34 | @Api(value = "后台设置控制器", tags = {"后台设置选项接口"}) 35 | public class OptionsController { 36 | 37 | @Autowired 38 | private OptionsService optionsService; 39 | 40 | @Autowired 41 | private MyBlogProperties properties; 42 | 43 | @ApiOperation("获取所有的博客设置") 44 | @GetMapping("/list") 45 | public R listAll() { 46 | Map optionsMap = optionsService.listAll(); 47 | return RUtils.success("所有的博客设置", optionsMap); 48 | } 49 | 50 | @ApiOperation("修改博客设置") 51 | @PostMapping("/update") 52 | public R update(@RequestBody Map optionMap) { 53 | // 修改成功的数量 54 | AtomicInteger count = new AtomicInteger(); 55 | optionMap.forEach((key, value) -> { 56 | if (StringUtils.hasText(key) && Objects.nonNull(value)) { 57 | int i = optionsService.setAnyway(key, String.valueOf(value)); 58 | if (i > 0) { 59 | count.getAndIncrement(); 60 | } 61 | // 保存 favicon 62 | // if (MyBlogOptionEnum.FAVICON.key().equals(key)) { 63 | // saveFavicon(String.valueOf(value)); 64 | // } 65 | } 66 | }); 67 | 68 | if (count.get() > 0) { 69 | optionsService.resetOptionsCache(); 70 | return RUtils.success("博客设置修改成功"); 71 | } 72 | 73 | return RUtils.success("博客设置未作修改"); 74 | } 75 | 76 | /** 77 | * 保存 favicon 到文件中 78 | */ 79 | @Async("executor") 80 | public void saveFavicon(String url) { 81 | String savePath = properties.getFileSavePath() + File.separatorChar + "favicon.ico"; 82 | 83 | try (BufferedInputStream bis = new BufferedInputStream( 84 | new URL(URLUtils.encodeAll(url)).openStream())) { 85 | // 不是图片 86 | String contentType = URLConnection.guessContentTypeFromStream(bis); 87 | if (!(StringUtils.hasText(contentType) && contentType.startsWith("image"))) { 88 | log.warn("not an image url: {}", url); 89 | return; 90 | } 91 | // 压缩图片 92 | Thumbnails.of(bis) 93 | .size(32, 32) 94 | .outputFormat("jpg") // ico不支持 95 | .toFile(savePath); 96 | } catch (Exception e) { 97 | log.warn("favicon.icon url is invalid."); 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/SpeciallistController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | import xyz.snwjas.blog.model.PageResult; 10 | import xyz.snwjas.blog.model.R; 11 | import xyz.snwjas.blog.model.entity.SpeciallistEntity; 12 | import xyz.snwjas.blog.model.enums.SpecialListType; 13 | import xyz.snwjas.blog.model.params.ListParam; 14 | import xyz.snwjas.blog.model.params.SpeciallistSearchParam; 15 | import xyz.snwjas.blog.model.vo.SpeciallistVO; 16 | import xyz.snwjas.blog.service.SpeciallistService; 17 | import xyz.snwjas.blog.utils.RUtils; 18 | 19 | import javax.validation.Valid; 20 | import javax.validation.constraints.NotNull; 21 | import java.util.List; 22 | 23 | /** 24 | * Log Controller 25 | * 26 | * @author Myles Yang 27 | */ 28 | @Validated 29 | @RestController("AdminSpeciallistController") 30 | @RequestMapping("/api/admin/speciallist") 31 | @Api(value = "后台特殊清单控制器", tags = {"后台特殊清单接口"}) 32 | public class SpeciallistController { 33 | 34 | @Autowired 35 | private SpeciallistService speciallistService; 36 | 37 | 38 | @PostMapping("/list") 39 | @ApiOperation("获取清单列表") 40 | public R listBlogs(@RequestBody @Valid SpeciallistSearchParam param) { 41 | IPage page = speciallistService.pageBy(param); 42 | PageResult pageResult = speciallistService.covertToPageResult(page); 43 | return RUtils.success("清单列表", pageResult); 44 | } 45 | 46 | @DeleteMapping("/delete") 47 | @ApiOperation("批量删除清单") 48 | public R delete(@RequestBody @Validated ListParam ids) { 49 | int i = speciallistService.deleteByIds(ids); 50 | return RUtils.commonFailOrNot(i, "批量删除清单"); 51 | } 52 | 53 | @PostMapping("/add") 54 | @ApiOperation("批量导入清单") 55 | public R addBatch(@RequestParam("type") @NotNull SpecialListType type, 56 | @RequestParam("content") @Validated ListParam contents) { 57 | boolean b = speciallistService.addBatch(type, contents); 58 | return RUtils.commonFailOrNot(b ? 1 : 0, "批量导入清单"); 59 | } 60 | 61 | @GetMapping("/types") 62 | @ApiOperation("列出所有已枚举清单类型") 63 | public R listTypes() { 64 | List types = speciallistService.listEnumType(); 65 | return RUtils.success("已枚举清单类型", types); 66 | } 67 | 68 | @GetMapping("/refresh-context/{type}") 69 | @ApiOperation("刷新上下文") 70 | public R refreshContext(@PathVariable("type") @NotNull SpecialListType type) { 71 | boolean succeed = speciallistService.refreshContext(type); 72 | return RUtils.commonFailOrNot(succeed ? 1 : 0, String.format("刷新上下文[%s]", type)); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/StatisticsController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.format.annotation.DateTimeFormat; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | import xyz.snwjas.blog.config.LocalDateTimeSerializerConfig; 13 | import xyz.snwjas.blog.model.R; 14 | import xyz.snwjas.blog.model.vo.StatisticsBasicVO; 15 | import xyz.snwjas.blog.model.vo.StatisticsReportVo; 16 | import xyz.snwjas.blog.service.impl.StatisticsServiceImpl; 17 | import xyz.snwjas.blog.utils.RUtils; 18 | 19 | import java.time.LocalDateTime; 20 | import java.util.List; 21 | 22 | /** 23 | * Admin Controller 24 | * 25 | * @author Myles Yang 26 | */ 27 | @Validated 28 | @RestController("AdminStatisticsController") 29 | @RequestMapping("/api/admin/statistics") 30 | @Api(value = "后台统计控制器", tags = {"后台统计接口"}) 31 | public class StatisticsController { 32 | 33 | @Autowired 34 | private StatisticsServiceImpl statisticsService; 35 | 36 | 37 | @GetMapping("/common") 38 | @ApiOperation("获取基本的统计信息") 39 | public R getCommonStatistics() { 40 | StatisticsBasicVO statisticsBasicVo = statisticsService.getCommonStatistics(); 41 | return RUtils.success("统计信息", statisticsBasicVo); 42 | } 43 | 44 | @GetMapping("/daily/{start}/{end}") 45 | @ApiOperation("获取每日的统计信息") 46 | public R getDailyStatistics(@PathVariable("start") @DateTimeFormat(pattern = 47 | LocalDateTimeSerializerConfig.DEFAULT_DATE_TIME_PATTERN) LocalDateTime start, 48 | @PathVariable("end") @DateTimeFormat(pattern = 49 | LocalDateTimeSerializerConfig.DEFAULT_DATE_TIME_PATTERN) LocalDateTime end) { 50 | List list = statisticsService.getDailyStatistics(start, end); 51 | return RUtils.success("每日的统计数据", list); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/admin/TagController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.admin; 2 | 3 | 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | import xyz.snwjas.blog.model.base.ValidGroupType; 10 | import xyz.snwjas.blog.model.vo.TagVO; 11 | import xyz.snwjas.blog.service.TagService; 12 | import xyz.snwjas.blog.model.R; 13 | import xyz.snwjas.blog.utils.RUtils; 14 | 15 | import javax.validation.constraints.Min; 16 | import javax.validation.constraints.NotNull; 17 | import java.util.List; 18 | 19 | /** 20 | *

21 | * 标签 前端控制器 22 | *

23 | * 24 | * @author Myles Yang 25 | */ 26 | @Validated 27 | @RestController("AdminTagController") 28 | @RequestMapping("/api/admin/blog/tag") 29 | @Api(value = "后台标签控制器", tags = {"后台标签接口"}) 30 | public class TagController { 31 | 32 | @Autowired 33 | private TagService tagService; 34 | 35 | @GetMapping("/list") 36 | @ApiOperation("获取所有的博客标签") 37 | public R listAllTags() { 38 | List tagVOList = tagService.listAll(); 39 | return RUtils.success("所有的博客标签", tagVOList); 40 | } 41 | 42 | @DeleteMapping("/delete/{tagId}") 43 | @ApiOperation("删除标签") 44 | public R delete(@PathVariable("tagId") @NotNull @Min(1) Integer tagId) { 45 | int i = tagService.deleteById(tagId); 46 | return RUtils.commonFailOrNot(i, "标签删除"); 47 | } 48 | 49 | @PostMapping("/add") 50 | @ApiOperation("添加标签") 51 | public R add(@RequestBody @Validated({ValidGroupType.Save.class}) TagVO vo) { 52 | int i = tagService.add(vo); 53 | if (i > 0) { 54 | return RUtils.success("标签添加成功"); 55 | } 56 | if (i < 0) { 57 | return RUtils.fail("标签名已存在"); 58 | } 59 | return RUtils.fail("标签添加失败"); 60 | } 61 | 62 | @PostMapping("/update") 63 | @ApiOperation("更新标签") 64 | public R update(@RequestBody @Validated({ValidGroupType.Update.class}) TagVO vo) { 65 | int i = tagService.update(vo); 66 | return RUtils.commonFailOrNot(i, "标签更新"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/app/BlogController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.app; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | import xyz.snwjas.blog.annotation.AccessLimit; 10 | import xyz.snwjas.blog.constant.CacheKeyPrefix; 11 | import xyz.snwjas.blog.constant.RS; 12 | import xyz.snwjas.blog.model.PageResult; 13 | import xyz.snwjas.blog.model.R; 14 | import xyz.snwjas.blog.model.entity.BlogEntity; 15 | import xyz.snwjas.blog.model.enums.BlogStatus; 16 | import xyz.snwjas.blog.model.params.BlogSearchParam; 17 | import xyz.snwjas.blog.model.vo.BlogArchiveVO; 18 | import xyz.snwjas.blog.model.vo.BlogDetailVO; 19 | import xyz.snwjas.blog.model.vo.BlogSimpleVO; 20 | import xyz.snwjas.blog.service.BlogService; 21 | import xyz.snwjas.blog.support.cache.MemoryCacheStore; 22 | import xyz.snwjas.blog.utils.IPUtils; 23 | import xyz.snwjas.blog.utils.RUtils; 24 | import xyz.snwjas.blog.utils.URLUtils; 25 | 26 | import javax.servlet.http.HttpServletRequest; 27 | import javax.validation.constraints.Min; 28 | import javax.validation.constraints.NotBlank; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Objects; 32 | 33 | /** 34 | * Blog Controller 35 | * 36 | * @author Myles Yang 37 | */ 38 | @Validated 39 | @RestController("AppBlogController") 40 | @RequestMapping("/api/app/blog") 41 | @Api(value = "前台博客控制器", tags = {"前台博客文章接口"}) 42 | public class BlogController { 43 | 44 | private static final int PAGE_SIZE = 10; 45 | 46 | @Autowired 47 | private BlogService blogService; 48 | 49 | @Autowired 50 | private MemoryCacheStore cache; 51 | 52 | @AccessLimit(maxCount = 2) 53 | @PostMapping("/list") 54 | @ApiOperation("条件获取博客文章列表") 55 | public R listBlogs(@RequestBody @Validated BlogSearchParam param) { 56 | 57 | param.setStatus(BlogStatus.PUBLISHED); 58 | param.setPageSize(PAGE_SIZE); 59 | 60 | IPage blogPage = blogService.pageBy(param); 61 | PageResult pageResult = blogService.covertToPageResult(blogPage); 62 | 63 | return RUtils.success("博客文章列表", pageResult); 64 | } 65 | 66 | @AccessLimit(maxCount = 1) 67 | @GetMapping("/list/achievement") 68 | @ApiOperation("获取文章归档") 69 | public R listAchievement() { 70 | Map> map = blogService.listArchive(); 71 | 72 | return RUtils.success("文章归档", map); 73 | } 74 | 75 | @AccessLimit(maxCount = 2) 76 | @GetMapping("/content/{blogUrl}") 77 | @ApiOperation("获取博客文章内容") 78 | public R getBlog(HttpServletRequest request, 79 | @PathVariable("blogUrl") @NotBlank String blogUrl) { 80 | 81 | String url = URLUtils.decode(blogUrl); 82 | BlogDetailVO blogContent = blogService.getByUrl(url); 83 | 84 | if (Objects.nonNull(blogContent)) { 85 | blogContent.setOriginalContent(""); 86 | 87 | // 统计博客访问人数 88 | String ipAddress = IPUtils.getIpAddress(request); 89 | // 缓存在会在 StatisticTask 中清除 90 | String key = CacheKeyPrefix.BLOG_VISIT_COUNT + blogContent.getId() 91 | + CacheKeyPrefix.SEPARATOR + ipAddress; 92 | cache.setIfAbsent(key, null); 93 | } 94 | 95 | return RUtils.success("博客文章内容", blogContent); 96 | } 97 | 98 | @AccessLimit(maxCount = 3) 99 | @GetMapping("/like/{blogId}") 100 | @ApiOperation("点赞博客") 101 | public R like(@PathVariable("blogId") @Min(1) Integer blogId) { 102 | int like = blogService.like(blogId); 103 | if (like == 0) { 104 | return RUtils.fail(RS.ILLEGAL_PARAMETER); 105 | } 106 | if (like == -1) { 107 | return RUtils.success("取消点赞", like); 108 | } 109 | return RUtils.success("点赞成功", like); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/app/CategoryController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.app; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import xyz.snwjas.blog.annotation.AccessLimit; 10 | import xyz.snwjas.blog.model.enums.BlogStatus; 11 | import xyz.snwjas.blog.model.vo.CategoryVO; 12 | import xyz.snwjas.blog.service.BlogService; 13 | import xyz.snwjas.blog.service.CategoryService; 14 | import xyz.snwjas.blog.model.R; 15 | import xyz.snwjas.blog.utils.RUtils; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * Category Controller 21 | * 22 | * @author Myles Yang 23 | */ 24 | @RestController("AppCategoryController") 25 | @RequestMapping("/api/app/category") 26 | @Api(value = "前台分类控制器", tags = {"前台分类接口"}) 27 | public class CategoryController { 28 | 29 | @Autowired 30 | private CategoryService categoryService; 31 | 32 | @Autowired 33 | private BlogService blogService; 34 | 35 | 36 | @AccessLimit(maxCount = 2) 37 | @GetMapping("/list") 38 | @ApiOperation("获取已使用的分类") 39 | public R listUsedCategory() { 40 | List usedCategoryList = categoryService.listUsedCategory(); 41 | // 获取每个分类下的文章数目 42 | for (CategoryVO vo : usedCategoryList) { 43 | int count = blogService.getCountByCategoryId(vo.getId(), BlogStatus.PUBLISHED); 44 | vo.setBlogCount(count); 45 | } 46 | return RUtils.success("分类列表", usedCategoryList); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/app/CommentController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.app; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | import xyz.snwjas.blog.annotation.AccessLimit; 10 | import xyz.snwjas.blog.model.PageResult; 11 | import xyz.snwjas.blog.model.R; 12 | import xyz.snwjas.blog.model.entity.CommentEntity; 13 | import xyz.snwjas.blog.model.enums.CommentStatus; 14 | import xyz.snwjas.blog.model.params.BasePageParam; 15 | import xyz.snwjas.blog.model.vo.CommentDetailVO; 16 | import xyz.snwjas.blog.model.vo.CommentSimpleVO; 17 | import xyz.snwjas.blog.service.CommentService; 18 | import xyz.snwjas.blog.support.wordfilter.WordFilter; 19 | import xyz.snwjas.blog.utils.RUtils; 20 | import xyz.snwjas.blog.utils.StrUtils; 21 | 22 | import javax.validation.constraints.Min; 23 | import javax.validation.constraints.NotNull; 24 | import java.util.HashMap; 25 | 26 | /** 27 | * Comment Controller 28 | * 29 | * @author Myles Yang 30 | */ 31 | @Validated 32 | @RestController("AppCommentController") 33 | @RequestMapping("/api/app/comment") 34 | @Api(value = "前台评论控制器", tags = {"前台评论接口"}) 35 | public class CommentController { 36 | 37 | private static final int PAGE_SIZE = 7; 38 | 39 | @Autowired 40 | private CommentService commentService; 41 | 42 | @Autowired 43 | private WordFilter wordFilter; 44 | 45 | @AccessLimit(maxCount = 5) 46 | @PostMapping("/list") 47 | @ApiOperation("获取评论列表") 48 | public R listComments(@RequestParam("bid") @NotNull @Min(1) Integer blogId, 49 | @RequestParam("pid") @NotNull @Min(0) Integer parentId, 50 | @RequestParam("cur") @NotNull @Min(1) Integer current) { 51 | 52 | BasePageParam param = new BasePageParam() 53 | .setCurrent(current) 54 | .setPageSize(PAGE_SIZE); 55 | IPage entityPage = commentService.pageBy(blogId, parentId, param); 56 | PageResult pageResult = commentService.covertToSimplePageResult(entityPage); 57 | 58 | return RUtils.success("评论列表", pageResult); 59 | } 60 | 61 | @AccessLimit(maxCount = 5) 62 | @PostMapping("/listr") 63 | @ApiOperation("顶层往下递归获取所有评论") 64 | public R listComments(@RequestParam("bid") @NotNull @Min(1) Integer blogId, 65 | @RequestParam("cur") @NotNull @Min(1) Integer current) { 66 | 67 | BasePageParam param = new BasePageParam() 68 | .setCurrent(current) 69 | .setPageSize(PAGE_SIZE); 70 | IPage entityPage = commentService.pageBy(blogId, param); 71 | PageResult pageResult = commentService.covertToSimplePageResultByRecursively(entityPage); 72 | 73 | // 获取评论总数 74 | int count = commentService.getCountByBlogIdAndStatus(blogId, CommentStatus.PUBLISHED); 75 | 76 | HashMap map = new HashMap<>(4); 77 | map.put("all", count); 78 | map.put("total", pageResult.getTotal()); 79 | map.put("list", pageResult.getList()); 80 | 81 | return RUtils.success("评论列表", map); 82 | } 83 | 84 | @AccessLimit(maxCount = 5) 85 | @PostMapping("/publish") 86 | @ApiOperation("发表评论") 87 | public R publishComment(@RequestBody @Validated(CommentSimpleVO.Guest.class) CommentDetailVO vo) { 88 | 89 | String author = StrUtils.removeBlank(vo.getAuthor()); 90 | vo.setAuthor(wordFilter.replace(author)); 91 | String content = StrUtils.removeBlank(vo.getContent()); 92 | vo.setContent(wordFilter.replace(content)); 93 | int i = commentService.reply(vo); 94 | return RUtils.commonFailOrNot(i, "评论发表"); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/app/IndexController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.app; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import xyz.snwjas.blog.annotation.AccessLimit; 10 | import xyz.snwjas.blog.constant.MyBlogOptionEnum; 11 | import xyz.snwjas.blog.model.vo.OptionVO; 12 | import xyz.snwjas.blog.model.vo.UserVO; 13 | import xyz.snwjas.blog.service.OptionsService; 14 | import xyz.snwjas.blog.service.StatisticsService; 15 | import xyz.snwjas.blog.service.UserService; 16 | import xyz.snwjas.blog.model.R; 17 | import xyz.snwjas.blog.model.UserDetail; 18 | import xyz.snwjas.blog.utils.RUtils; 19 | 20 | import java.time.LocalDateTime; 21 | import java.util.Map; 22 | 23 | /** 24 | * Index Controller 25 | * 26 | * @author Myles Yang 27 | */ 28 | @RestController("AppIndexController") 29 | @RequestMapping("/api/app") 30 | @Api(value = "前台通用控制器", tags = {"前台通用接口"}) 31 | public class IndexController { 32 | 33 | @Autowired 34 | private OptionsService optionsService; 35 | 36 | @Autowired 37 | private StatisticsService statisticsService; 38 | 39 | @Autowired 40 | private UserService userService; 41 | 42 | @AccessLimit(maxCount = 1) 43 | @GetMapping("/attributes") 44 | @ApiOperation("获取博客的基本属性") 45 | public R getAttributes() { 46 | 47 | Map attributes = optionsService.listNecessary(); 48 | 49 | // 建站日期 50 | MyBlogOptionEnum birthdayEnum = MyBlogOptionEnum.BIRTHDAY; 51 | OptionVO birthdayVO = optionsService.get(birthdayEnum.key()); 52 | LocalDateTime birthday = LocalDateTime.parse(birthdayVO.getOptionValue()); 53 | attributes.put(birthdayEnum.key(), birthday); 54 | 55 | // 页脚 56 | MyBlogOptionEnum footerEnum = MyBlogOptionEnum.FOOTER; 57 | OptionVO footerVO = optionsService.get(footerEnum.key()); 58 | attributes.put(footerEnum.key(), footerVO.getOptionValue()); 59 | 60 | // 网站访问数量 61 | int webTotalVisit = statisticsService.getWebTotalVisit(); 62 | attributes.put("visit", webTotalVisit); 63 | 64 | return RUtils.success("基本属性", attributes); 65 | } 66 | 67 | @AccessLimit(maxCount = 1) 68 | @GetMapping("/about-me") 69 | @ApiOperation("关于我") 70 | public R getAboutMe() { 71 | // 确保账号id为1 72 | UserDetail userDetail = userService.getByUserId(1); 73 | UserVO userVO = new UserVO().convertFrom(userDetail); 74 | userVO.setUsername(""); 75 | 76 | return RUtils.success("关于我", userVO); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/app/LinkController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.app; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.swagger.annotations.Api; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import xyz.snwjas.blog.annotation.AccessLimit; 11 | import xyz.snwjas.blog.model.entity.LinkEntity; 12 | import xyz.snwjas.blog.model.params.LinkSearchParam; 13 | import xyz.snwjas.blog.model.vo.LinkVO; 14 | import xyz.snwjas.blog.service.LinkService; 15 | import xyz.snwjas.blog.model.R; 16 | import xyz.snwjas.blog.utils.RUtils; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * Link Controller 22 | * 23 | * @author Myles Yang 24 | */ 25 | @RestController("AppLinkController") 26 | @RequestMapping("/api/app/link") 27 | @Api(value = "前台友链控制器", tags = {"前台友链接口"}) 28 | public class LinkController { 29 | 30 | @Autowired 31 | private LinkService linkService; 32 | 33 | @AccessLimit(maxCount = 2) 34 | @GetMapping("/list") 35 | @ApiOperation("获取所有的友情链接") 36 | public R listUsedCategory() { 37 | LinkSearchParam param = new LinkSearchParam(); 38 | param.setCurrent(1).setPageSize(Integer.MAX_VALUE); 39 | IPage linkPage = linkService.pageBy(param); 40 | List linkVOList = linkService.covertToListVO(linkPage.getRecords()); 41 | return RUtils.success("友链列表", linkVOList); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/controller/app/TagController.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.controller.app; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.validation.annotation.Validated; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import xyz.snwjas.blog.annotation.AccessLimit; 11 | import xyz.snwjas.blog.model.vo.TagVO; 12 | import xyz.snwjas.blog.service.TagService; 13 | import xyz.snwjas.blog.model.R; 14 | import xyz.snwjas.blog.utils.RUtils; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * Tag Controller 20 | * 21 | * @author Myles Yang 22 | */ 23 | @Validated 24 | @RestController("AppTagController") 25 | @RequestMapping("/api/app/tag") 26 | @Api(value = "前台标签控制器", tags = {"前台标签接口"}) 27 | public class TagController { 28 | 29 | @Autowired 30 | private TagService tagService; 31 | 32 | @AccessLimit(maxCount = 2) 33 | @GetMapping("/list") 34 | @ApiOperation("获取全部已使用的博客标签") 35 | public R listUsed() { 36 | List tagVOList = tagService.listUsed(); 37 | return RUtils.success("博客标签", tagVOList); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/exception/MyBlogException.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.exception; 2 | 3 | /** 4 | * Base exception of the project. 5 | * 6 | * @author Myles Yang 7 | */ 8 | public abstract class MyBlogException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = 4140200838147465959L; 11 | 12 | private Object errorData; 13 | 14 | public MyBlogException(String message) { 15 | super(message); 16 | } 17 | 18 | public MyBlogException(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | public abstract int getStatus(); 23 | 24 | public Object getErrorData() { 25 | return errorData; 26 | } 27 | 28 | public void setErrorData(Object errorData) { 29 | this.errorData = errorData; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/exception/ServiceException.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import xyz.snwjas.blog.constant.RS; 5 | 6 | /** 7 | * 服务异常 8 | * 9 | * @author Myles Yang 10 | */ 11 | public class ServiceException extends MyBlogException { 12 | 13 | private static final long serialVersionUID = 7951201720502956459L; 14 | 15 | private final int status; 16 | 17 | public ServiceException() { 18 | super(RS.SYSTEM_ERROR.message()); 19 | this.status = RS.SYSTEM_ERROR.status(); 20 | } 21 | 22 | public ServiceException(String message) { 23 | super(message); 24 | this.status = RS.SYSTEM_ERROR.status(); 25 | } 26 | 27 | public ServiceException(String message, Throwable cause) { 28 | super(message, cause); 29 | this.status = RS.SYSTEM_ERROR.status(); 30 | } 31 | 32 | public ServiceException(RS status) { 33 | super(status.message()); 34 | this.status = status.status(); 35 | } 36 | 37 | public ServiceException(HttpStatus status) { 38 | super(status.getReasonPhrase()); 39 | this.status = status.value(); 40 | } 41 | 42 | @Override 43 | public int getStatus() { 44 | return status; 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/interceptor/AccessLimitInterceptor.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.interceptor; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.method.HandlerMethod; 5 | import org.springframework.web.servlet.HandlerInterceptor; 6 | import xyz.snwjas.blog.annotation.AccessLimit; 7 | import xyz.snwjas.blog.constant.CacheKeyPrefix; 8 | import xyz.snwjas.blog.constant.RS; 9 | import xyz.snwjas.blog.exception.ServiceException; 10 | import xyz.snwjas.blog.support.cache.MemoryCacheStore; 11 | import xyz.snwjas.blog.utils.IPUtils; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.lang.reflect.Method; 16 | import java.util.Objects; 17 | 18 | /** 19 | * 接口限流防刷拦截器 20 | * 21 | * @author Myles Yang 22 | */ 23 | @Component 24 | public class AccessLimitInterceptor implements HandlerInterceptor { 25 | 26 | private final MemoryCacheStore memoryCacheStore; 27 | 28 | public AccessLimitInterceptor(MemoryCacheStore memoryCacheStore) { 29 | this.memoryCacheStore = memoryCacheStore; 30 | } 31 | 32 | @Override 33 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 34 | if (!(handler instanceof HandlerMethod)) { 35 | return true; 36 | } 37 | 38 | HandlerMethod handlerMethod = (HandlerMethod) handler; 39 | Method method = handlerMethod.getMethod(); 40 | 41 | AccessLimit annotation = method.getAnnotation(AccessLimit.class); 42 | if (Objects.nonNull(annotation)) { 43 | check(annotation, request); 44 | } 45 | 46 | return true; 47 | } 48 | 49 | private void check(AccessLimit annotation, HttpServletRequest request) { 50 | 51 | int maxCount = annotation.maxCount(); 52 | int seconds = annotation.seconds(); 53 | 54 | String key = CacheKeyPrefix.ACCESS_LIMIT_PREFIX 55 | + IPUtils.getIpAddress(request) 56 | + request.getRequestURI(); 57 | 58 | Integer count = (Integer) memoryCacheStore.get(key); 59 | if (Objects.nonNull(count)) { 60 | if (count < maxCount) { 61 | memoryCacheStore.set(key, count + 1, seconds); 62 | } else { 63 | throw new ServiceException(RS.FREQUENT_OPERATION); 64 | } 65 | } else { 66 | memoryCacheStore.set(key, 1, seconds); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/interceptor/StatisticInterceptor.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.interceptor; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.web.servlet.HandlerInterceptor; 7 | import xyz.snwjas.blog.constant.CacheKeyPrefix; 8 | import xyz.snwjas.blog.support.cache.MemoryCacheStore; 9 | import xyz.snwjas.blog.utils.IPUtils; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | /** 15 | * Statistic Interceptor 16 | * 17 | * @author Myles Yang 18 | */ 19 | @Component 20 | @Slf4j 21 | public class StatisticInterceptor implements HandlerInterceptor { 22 | 23 | @Autowired 24 | private MemoryCacheStore cache; 25 | 26 | @Override 27 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 28 | 29 | // 统计网站访问人数 30 | 31 | String ipAddress = IPUtils.getIpAddress(request); 32 | 33 | // 缓存在 StatisticTask 中清除 34 | String webVisitKey = CacheKeyPrefix.WEB_VISIT_COUNT + ipAddress; 35 | cache.setIfAbsent(webVisitKey, null); 36 | 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/AttachmentMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import xyz.snwjas.blog.model.entity.AttachmentEntity; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 附件 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface AttachmentMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/BlogMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.baomidou.mybatisplus.core.toolkit.Constants; 6 | import org.apache.ibatis.annotations.Param; 7 | import xyz.snwjas.blog.model.entity.BlogEntity; 8 | 9 | /** 10 | *

11 | * 博客 Mapper 接口 12 | *

13 | * 14 | * @author Myles Yang 15 | */ 16 | public interface BlogMapper extends BaseMapper { 17 | 18 | /** 19 | * 对某列求和 20 | * @param column 列名 21 | * @param wrapper 条件构造器 22 | * @return 列和 23 | */ 24 | Integer sum(@Param("column") String column, @Param(Constants.WRAPPER) Wrapper wrapper); 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/BlogTagMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import xyz.snwjas.blog.model.entity.BlogTagEntity; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 博客标签 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface BlogTagMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import xyz.snwjas.blog.model.entity.CategoryEntity; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 分类 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface CategoryMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/CommentMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import xyz.snwjas.blog.model.entity.CommentEntity; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 评论 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface CommentMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/LinkMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import xyz.snwjas.blog.model.entity.LinkEntity; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | 7 | /** 8 | *

9 | * 友链 Mapper 接口 10 | *

11 | * 12 | * @author Myles Yang 13 | */ 14 | public interface LinkMapper extends BaseMapper { 15 | 16 | /** 17 | * 根据Logo解析器更新全部记录Logo 18 | */ 19 | Integer updateLogoByParser(@Param("parser") String parser); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/LogMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import xyz.snwjas.blog.model.entity.LogEntity; 5 | 6 | /** 7 | *

8 | * 日志 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface LogMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/OptionsMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import xyz.snwjas.blog.model.entity.OptionsEntity; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 系统(博客)设置 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface OptionsMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/SpecaillistMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import xyz.snwjas.blog.model.entity.SpeciallistEntity; 5 | 6 | /** 7 | *

8 | * 特殊清单 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface SpecaillistMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/StatisticsMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.baomidou.mybatisplus.core.toolkit.Constants; 6 | import org.apache.ibatis.annotations.Param; 7 | import xyz.snwjas.blog.model.entity.BlogEntity; 8 | import xyz.snwjas.blog.model.entity.StatisticsEntity; 9 | 10 | 11 | /** 12 | *

13 | * 统计表(统计每日的数据) Mapper 接口 14 | *

15 | * 16 | * @author Myles Yang 17 | */ 18 | public interface StatisticsMapper extends BaseMapper { 19 | 20 | /** 21 | * 对某列求和 22 | * @param column 列名 23 | * @param wrapper 条件构造器 24 | * @return 列和 25 | */ 26 | Integer sum(@Param("column") String column, @Param(Constants.WRAPPER) Wrapper wrapper); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/TagMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import xyz.snwjas.blog.model.entity.TagEntity; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * 标签 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface TagMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import xyz.snwjas.blog.model.entity.UserEntity; 5 | 6 | /** 7 | *

8 | * 用户 Mapper 接口 9 | *

10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface UserMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/AttachmentMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/BlogMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/BlogTagMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/CategoryMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/CommentMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/LinkMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | update link 7 | set logo = replace(#{parser} , '{}' , url) 8 | where 1=1 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/LogMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/OptionsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/SpeciallistMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/StatisticsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/TagMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/mapper/xml/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/PageResult.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import org.springframework.lang.NonNull; 7 | 8 | import java.io.Serializable; 9 | import java.util.List; 10 | 11 | /** 12 | * 分页结果 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Data 17 | @ApiModel("分页结果") 18 | public class PageResult implements Serializable { 19 | 20 | private static final long serialVersionUID = -3862949514414749919L; 21 | 22 | @ApiModelProperty("数据总条数") 23 | private long total; 24 | 25 | @ApiModelProperty("分页数据") 26 | private List list; 27 | 28 | public PageResult(@NonNull List list) { 29 | this.total = list.size(); 30 | this.list = list; 31 | } 32 | 33 | public PageResult(long total, @NonNull List list) { 34 | this.total = total; 35 | this.list = list; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/R.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | import java.time.LocalDateTime; 9 | 10 | /** 11 | * 统一响应体 12 | * 13 | * @author Myles Yang 14 | */ 15 | @Data 16 | @ApiModel("统一响应结果") 17 | public class R implements Serializable { 18 | 19 | private static final long serialVersionUID = -637415113996231595L; 20 | 21 | @ApiModelProperty("响应时间") 22 | private LocalDateTime timestamp; 23 | 24 | @ApiModelProperty("响应状态值") 25 | private int status; 26 | 27 | @ApiModelProperty("响应消息") 28 | private String message; 29 | 30 | @ApiModelProperty("响应数据") 31 | private Object data; 32 | 33 | 34 | public R(int status, String message, Object data) { 35 | this.timestamp = LocalDateTime.now(); 36 | this.status = status; 37 | this.message = message; 38 | this.data = data; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/UserDetail.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model; 2 | 3 | import lombok.Data; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.authority.AuthorityUtils; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import xyz.snwjas.blog.model.base.BeanConvert; 8 | 9 | import java.util.Collection; 10 | 11 | /** 12 | * SpringSecurity 中认证的用户核心信息 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Data 17 | public class UserDetail implements UserDetails, BeanConvert { 18 | 19 | private static final long serialVersionUID = -1339790632097707968L; 20 | 21 | private Integer id; 22 | 23 | private String username; 24 | 25 | private String password; 26 | 27 | private String nickname; 28 | 29 | private String email; 30 | 31 | private String avatar; 32 | 33 | private String description; 34 | 35 | @Override 36 | public Collection getAuthorities() { 37 | return AuthorityUtils.createAuthorityList("admin"); 38 | } 39 | 40 | @Override 41 | public String getPassword() { 42 | return password; 43 | } 44 | 45 | @Override 46 | public boolean isAccountNonExpired() { 47 | return true; 48 | } 49 | 50 | @Override 51 | public boolean isAccountNonLocked() { 52 | return true; 53 | } 54 | 55 | @Override 56 | public boolean isCredentialsNonExpired() { 57 | return true; 58 | } 59 | 60 | @Override 61 | public boolean isEnabled() { 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/base/BeanConvert.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.base; 2 | 3 | 4 | import org.springframework.beans.BeanUtils; 5 | import org.springframework.lang.NonNull; 6 | 7 | /** 8 | * po dto vo ... bean转换 9 | * 10 | * @author Myles Yang 11 | */ 12 | public interface BeanConvert{ 13 | 14 | @SuppressWarnings("unchecked") 15 | default T convertFrom(@NonNull Object source, String... ignoreProperties) { 16 | if (source == null) { 17 | return null; 18 | } 19 | BeanUtils.copyProperties(source, this, ignoreProperties); 20 | return (T) this; 21 | } 22 | 23 | default T convertTo(@NonNull T target, String... ignoreProperties) { 24 | if (target == null) { 25 | return null; 26 | } 27 | BeanUtils.copyProperties(this, target, ignoreProperties); 28 | return target; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/base/ValidGroupType.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.base; 2 | 3 | /** 4 | * 参数校验分组 5 | * 根据不同的操作类型检验参数 6 | * 7 | * @author Myles Yang 8 | */ 9 | public interface ValidGroupType { 10 | 11 | // 保存时的检验 12 | interface Save { 13 | } 14 | 15 | // 更新时的检验 16 | interface Update { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/AttachmentEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 附件 16 | *

17 | * 18 | * @author Myles Yang 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Accessors(chain = true) 23 | @TableName("attachment") 24 | @ApiModel(value = "AttachmentEntity对象", description = "附件") 25 | public class AttachmentEntity extends BaseEntity { 26 | 27 | private static final long serialVersionUID = 6442928839623298459L; 28 | 29 | @ApiModelProperty(value = "文件id") 30 | @TableId(value = "id", type = IdType.AUTO) 31 | private Integer id; 32 | 33 | @ApiModelProperty(value = "文件名(包括后缀)") 34 | @TableField("name") 35 | private String name; 36 | 37 | @ApiModelProperty(value = "文件大小(字节)") 38 | @TableField("size") 39 | private Long size; 40 | 41 | @ApiModelProperty(value = "文件路径") 42 | @TableField("path") 43 | private String path; 44 | 45 | @ApiModelProperty(value = "html资源类型") 46 | @TableField("media_type") 47 | private String mediaType; 48 | 49 | @ApiModelProperty(value = "文件缩略图路径") 50 | @TableField("thumb_path") 51 | private String thumbPath; 52 | 53 | @ApiModelProperty(value = "文件分组") 54 | @TableField("team") 55 | private String team; 56 | 57 | @ApiModelProperty(value = "文件为图片时,图片的宽度像素") 58 | @TableField(value = "width") 59 | private Integer width; 60 | 61 | @ApiModelProperty(value = "文件为图片时,图片的高度像素") 62 | @TableField(value = "height") 63 | private Integer height; 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * 基础实体 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Data 17 | @EqualsAndHashCode 18 | public class BaseEntity implements Serializable { 19 | 20 | private static final long serialVersionUID = -6489406991618570904L; 21 | 22 | @ApiModelProperty(value = "更新时间") 23 | @TableField(value = "update_time") 24 | private LocalDateTime updateTime; 25 | 26 | @ApiModelProperty(value = "创建时间") 27 | @TableField(value = "create_time") 28 | private LocalDateTime createTime; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/BlogEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | import xyz.snwjas.blog.model.enums.BlogCommentAllowStatus; 13 | import xyz.snwjas.blog.model.enums.BlogStatus; 14 | 15 | /** 16 | *

17 | * 博客 18 | *

19 | * 20 | * @author Myles Yang 21 | */ 22 | @Data 23 | @EqualsAndHashCode(callSuper = true) 24 | @Accessors(chain = true) 25 | @TableName("blog") 26 | @ApiModel(value = "BlogEntity对象", description = "博客") 27 | public class BlogEntity extends BaseEntity { 28 | 29 | private static final long serialVersionUID = 2193309050912718282L; 30 | 31 | @ApiModelProperty(value = "博客id") 32 | @TableId(value = "id", type = IdType.AUTO) 33 | private Integer id; 34 | 35 | @ApiModelProperty(value = "博客标题") 36 | @TableField("title") 37 | private String title; 38 | 39 | @ApiModelProperty(value = "原格式博客内容") 40 | @TableField("original_content") 41 | private String originalContent; 42 | 43 | @ApiModelProperty(value = "格式化(html)博客内容") 44 | @TableField("format_content") 45 | private String formatContent; 46 | 47 | @ApiModelProperty(value = "博客链接") 48 | @TableField("url") 49 | private String url; 50 | 51 | @ApiModelProperty(value = "博客摘要") 52 | @TableField("summary") 53 | private String summary; 54 | 55 | @ApiModelProperty(value = "博客缩略图链接") 56 | @TableField("thumbnail") 57 | private String thumbnail; 58 | 59 | @ApiModelProperty(value = "博客置顶排行") 60 | @TableField("top_rank") 61 | private Integer topRank; 62 | 63 | @ApiModelProperty(value = "是否允许评论,默认(1,允许但需审核)") 64 | @TableField("allow_comment") 65 | private BlogCommentAllowStatus allowComment; 66 | 67 | @ApiModelProperty(value = "喜欢的人数") 68 | @TableField("likes") 69 | private Integer likes; 70 | 71 | @ApiModelProperty(value = "博客访问人数") 72 | @TableField("visits") 73 | private Integer visits; 74 | 75 | @ApiModelProperty(value = "博客状态") 76 | @TableField("status") 77 | private BlogStatus status; 78 | 79 | @ApiModelProperty(value = "博客分类ID") 80 | @TableField("category_id") 81 | private Integer categoryId; 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/BlogTagEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 博客标签 16 | *

17 | * 18 | * @author Myles Yang 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Accessors(chain = true) 23 | @TableName("blog_tag") 24 | @ApiModel(value = "BlogTagEntity对象", description = "博客标签") 25 | public class BlogTagEntity extends BaseEntity { 26 | 27 | private static final long serialVersionUID = 437792569342041771L; 28 | 29 | @ApiModelProperty(value = "博客标签id") 30 | @TableId(value = "id", type = IdType.AUTO) 31 | private Integer id; 32 | 33 | @ApiModelProperty(value = "标签id") 34 | @TableField("tag_id") 35 | private Integer tagId; 36 | 37 | @ApiModelProperty(value = "博客id") 38 | @TableField("blog_id") 39 | private Integer blogId; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/CategoryEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 分类 16 | *

17 | * 18 | * @author Myles Yang 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Accessors(chain = true) 23 | @TableName("category") 24 | @ApiModel(value = "CategoryEntity对象", description = "分类") 25 | public class CategoryEntity extends BaseEntity { 26 | 27 | private static final long serialVersionUID = -524653268318328366L; 28 | 29 | @ApiModelProperty(value = "分类id") 30 | @TableId(value = "id", type = IdType.AUTO) 31 | private Integer id; 32 | 33 | @ApiModelProperty(value = "分类名") 34 | @TableField("name") 35 | private String name; 36 | 37 | @ApiModelProperty(value = "父分类id") 38 | @TableField("parent_id") 39 | private Integer parentId; 40 | 41 | @ApiModelProperty(value = "分类描述") 42 | @TableField("description") 43 | private String description; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/CommentEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | import xyz.snwjas.blog.model.enums.CommentStatus; 13 | 14 | /** 15 | *

16 | * 评论 17 | *

18 | * 19 | * @author Myles Yang 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = true) 23 | @Accessors(chain = true) 24 | @TableName("comment") 25 | @ApiModel(value = "CommentEntity对象", description = "评论") 26 | public class CommentEntity extends BaseEntity { 27 | 28 | private static final long serialVersionUID = -9064254063786654890L; 29 | 30 | @ApiModelProperty(value = "评论id") 31 | @TableId(value = "id", type = IdType.AUTO) 32 | private Integer id; 33 | 34 | @ApiModelProperty(value = "父评论id") 35 | @TableField("parent_id") 36 | private Integer parentId; 37 | 38 | @ApiModelProperty(value = "评论内容") 39 | @TableField("content") 40 | private String content; 41 | 42 | @ApiModelProperty(value = "评论作者") 43 | @TableField("author") 44 | private String author; 45 | 46 | @ApiModelProperty(value = "评论作者邮箱") 47 | @TableField("email") 48 | private String email; 49 | 50 | @ApiModelProperty(value = "评论作者头像") 51 | @TableField("avatar") 52 | private String avatar; 53 | 54 | @ApiModelProperty(value = "评论作者的ip地址") 55 | @TableField("ip_address") 56 | private Integer ipAddress; 57 | 58 | @ApiModelProperty(value = "评论作者的用户代理") 59 | @TableField("user_agent") 60 | private String userAgent; 61 | 62 | @ApiModelProperty(value = "博客id") 63 | @TableField("blog_id") 64 | private Integer blogId; 65 | 66 | @ApiModelProperty(value = "评论状态") 67 | @TableField("status") 68 | private CommentStatus status; 69 | 70 | @ApiModelProperty(value = "是否是管理员评论,默认false(0)") 71 | @TableField("is_admin") 72 | private Boolean isAdmin; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/LinkEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 友链 16 | *

17 | * 18 | * @author Myles Yang 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Accessors(chain = true) 23 | @TableName("link") 24 | @ApiModel(value = "LinksEntity对象", description = "友链") 25 | public class LinkEntity extends BaseEntity { 26 | 27 | private static final long serialVersionUID = 4547836104191474484L; 28 | 29 | @ApiModelProperty(value = "友链id") 30 | @TableId(value = "id", type = IdType.AUTO) 31 | private Integer id; 32 | 33 | @ApiModelProperty(value = "友链名称") 34 | @TableField("name") 35 | private String name; 36 | 37 | @ApiModelProperty(value = "友链链接") 38 | @TableField("url") 39 | private String url; 40 | 41 | @ApiModelProperty(value = "友链logo") 42 | @TableField("logo") 43 | private String logo; 44 | 45 | @ApiModelProperty(value = "友链排行") 46 | @TableField("top_rank") 47 | private Integer topRank; 48 | 49 | @ApiModelProperty(value = "友链描述") 50 | @TableField("description") 51 | private String description; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/LogEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | import xyz.snwjas.blog.model.enums.LogType; 13 | 14 | /** 15 | *

16 | * 日志 17 | *

18 | * 19 | * @author Myles Yang 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = true) 23 | @Accessors(chain = true) 24 | @TableName("log") 25 | @ApiModel(value = "LogEntity对象", description = "日志") 26 | public class LogEntity extends BaseEntity { 27 | 28 | private static final long serialVersionUID = -217049419018602936L; 29 | 30 | @ApiModelProperty(value = "日志id") 31 | @TableId(value = "id", type = IdType.AUTO) 32 | private Integer id; 33 | 34 | @ApiModelProperty(value = "操作内容") 35 | @TableField("content") 36 | private String content; 37 | 38 | @ApiModelProperty(value = "操作类型") 39 | @TableField("type") 40 | private LogType type; 41 | 42 | @ApiModelProperty(value = "操作人的ipv4地址,整型") 43 | @TableField("ip_address") 44 | private Integer ipAddress; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/OptionsEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 系统(博客)设置 16 | *

17 | * 18 | * @author Myles Yang 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Accessors(chain = true) 23 | @TableName("options") 24 | @ApiModel(value = "OptionEntity对象", description = "系统(博客)设置") 25 | public class OptionsEntity extends BaseEntity { 26 | 27 | private static final long serialVersionUID = -7423338560153552839L; 28 | 29 | @ApiModelProperty(value = "系统设置id") 30 | @TableId(value = "id", type = IdType.AUTO) 31 | private Integer id; 32 | 33 | @ApiModelProperty(value = "键") 34 | @TableField("option_key") 35 | private String optionKey; 36 | 37 | @ApiModelProperty(value = "值") 38 | @TableField("option_value") 39 | private String optionValue; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/SpeciallistEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.NoArgsConstructor; 12 | import lombok.experimental.Accessors; 13 | import xyz.snwjas.blog.model.enums.SpecialListType; 14 | 15 | /** 16 | *

17 | * 特殊名单 18 | *

19 | * 20 | * @author Myles Yang 21 | */ 22 | @NoArgsConstructor 23 | @Data 24 | @EqualsAndHashCode(callSuper = true) 25 | @Accessors(chain = true) 26 | @TableName("speciallist") 27 | @ApiModel(value = "SpeciallistEntity对象", description = "特殊名单表") 28 | public class SpeciallistEntity extends BaseEntity { 29 | 30 | private static final long serialVersionUID = -8025860806817409251L; 31 | 32 | @ApiModelProperty(value = "id") 33 | @TableId(value = "id", type = IdType.AUTO) 34 | private Integer id; 35 | 36 | @ApiModelProperty(value = "类型") 37 | @TableField("type") 38 | private SpecialListType type; 39 | 40 | @ApiModelProperty(value = "内容") 41 | @TableField("content") 42 | private String content; 43 | 44 | public SpeciallistEntity(SpecialListType type, String content) { 45 | this.type = type; 46 | this.content = content; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/StatisticsEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | *

14 | * 统计表(统计每日的数据) 15 | *

16 | * 17 | * @author Myles Yang 18 | */ 19 | @Data 20 | @EqualsAndHashCode(callSuper = true) 21 | @Accessors(chain = true) 22 | @TableName("statistics") 23 | @ApiModel(value = "StatisticsEntity对象", description = "统计表(统计每日的数据)") 24 | public class StatisticsEntity extends BaseEntity { 25 | 26 | private static final long serialVersionUID = -2832150898756464414L; 27 | 28 | @ApiModelProperty(value = "id") 29 | @TableId(value = "id", type = IdType.AUTO) 30 | private Integer id; 31 | 32 | @ApiModelProperty(value = "网站访问量") 33 | @TableField("web_visit_count") 34 | private Integer webVisitCount; 35 | 36 | @ApiModelProperty(value = "文章访问量") 37 | @TableField("blog_visit_count") 38 | private Integer blogVisitCount; 39 | 40 | @ApiModelProperty(value = "评论数量") 41 | @TableField("comment_count") 42 | private Integer commentCount; 43 | 44 | @ApiModelProperty(value = "统计日期") 45 | @TableField("date") 46 | private LocalDateTime date; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/TagEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 标签 16 | *

17 | * 18 | * @author Myles Yang 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Accessors(chain = true) 23 | @TableName("tag") 24 | @ApiModel(value = "TagEntity对象", description = "标签") 25 | public class TagEntity extends BaseEntity { 26 | 27 | private static final long serialVersionUID = 2913936447152558511L; 28 | 29 | @ApiModelProperty(value = "标签id") 30 | @TableId(value = "id", type = IdType.AUTO) 31 | private Integer id; 32 | 33 | @ApiModelProperty(value = "标签名") 34 | @TableField("name") 35 | private String name; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/entity/UserEntity.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 用户 16 | *

17 | * 18 | * @author Myles Yang 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Accessors(chain = true) 23 | @TableName("user") 24 | @ApiModel(value = "UserEntity对象", description = "用户") 25 | public class UserEntity extends BaseEntity { 26 | 27 | private static final long serialVersionUID = 1257044767231325225L; 28 | 29 | @ApiModelProperty(value = "用户id") 30 | @TableId(value = "id", type = IdType.AUTO) 31 | private Integer id; 32 | 33 | @ApiModelProperty(value = "登录用户名") 34 | @TableField("username") 35 | private String username; 36 | 37 | @ApiModelProperty(value = "登录密码") 38 | @TableField("password") 39 | private String password; 40 | 41 | @ApiModelProperty(value = "用户昵称") 42 | @TableField("nickname") 43 | private String nickname; 44 | 45 | @ApiModelProperty(value = "用户邮箱") 46 | @TableField("email") 47 | private String email; 48 | 49 | @ApiModelProperty(value = "用户头像链接") 50 | @TableField("avatar") 51 | private String avatar; 52 | 53 | @ApiModelProperty(value = "个人描述") 54 | @TableField("description") 55 | private String description; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/enums/BlogCommentAllowStatus.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | 5 | /** 6 | * 博客评论允许状态枚举常量 7 | * 8 | * @author Myles Yang 9 | */ 10 | public enum BlogCommentAllowStatus implements ValueEnum { 11 | /** 12 | * 不允许 13 | */ 14 | UNALLOWED(0), 15 | 16 | /** 17 | * 允许,但需审核 18 | */ 19 | ALLOWED_AUDITING(1), 20 | 21 | /** 22 | * 允许,自动审核 23 | */ 24 | ALLOWED_PASSAUTO(2); 25 | 26 | @EnumValue 27 | private final int value; 28 | 29 | BlogCommentAllowStatus(int value) { 30 | this.value = value; 31 | } 32 | 33 | @Override 34 | public Integer getValue() { 35 | return value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/enums/BlogStatus.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import com.baomidou.mybatisplus.core.enums.IEnum; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * 博客状态枚举常量 9 | * 10 | * @author Myles Yang 11 | */ 12 | public enum BlogStatus implements ValueEnum { 13 | /** 14 | * 已发表 15 | */ 16 | PUBLISHED(0), 17 | /** 18 | * 草稿 19 | */ 20 | DRAFT(1), 21 | /** 22 | * 回收站 23 | */ 24 | RECYCLE(2); 25 | 26 | @EnumValue 27 | private final int value; 28 | 29 | BlogStatus(int value) { 30 | this.value = value; 31 | } 32 | 33 | @Override 34 | public Integer getValue() { 35 | return value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/enums/CommentStatus.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | 5 | /** 6 | * 评论状态枚举常量 7 | * 8 | * @author Myles Yang 9 | */ 10 | public enum CommentStatus implements ValueEnum { 11 | /** 12 | * 审核中 13 | */ 14 | AUDITING(0), 15 | 16 | /** 17 | * 已发布 18 | */ 19 | PUBLISHED(1), 20 | 21 | /** 22 | * 回收站 23 | */ 24 | RECYCLE(2); 25 | 26 | @EnumValue 27 | private final Integer value; 28 | 29 | CommentStatus(Integer value) { 30 | this.value = value; 31 | } 32 | 33 | @Override 34 | public Integer getValue() { 35 | return value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/enums/LogType.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | 5 | /** 6 | * Log Type enum 7 | * 8 | * @author Myles Yang 9 | */ 10 | public enum LogType implements ValueEnum { 11 | 12 | COMMON(0), 13 | 14 | LOGGED_IN(1), 15 | 16 | LOGGED_OUT(2), 17 | 18 | LOGIN_FAILED(3), 19 | 20 | PASSWORD_UPDATED(4), 21 | 22 | PROFILE_UPDATED(5), 23 | 24 | BLOG_PUBLISHED(6), 25 | 26 | BLOG_EDITED(7), 27 | 28 | BLOG_DELETED(8), 29 | 30 | OPTION_UPDATE(9), 31 | ; 32 | 33 | @EnumValue 34 | private final Integer value; 35 | 36 | LogType(Integer value) { 37 | this.value = value; 38 | } 39 | 40 | @Override 41 | public Integer getValue() { 42 | return value; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/enums/SpecialListType.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | 5 | /** 6 | * Special List Type enum 7 | * 8 | * @author Myles Yang 9 | */ 10 | public enum SpecialListType implements ValueEnum { 11 | 12 | WORD_BLACK_LIST(1), 13 | 14 | WORD_WHITE_LIST(2), 15 | 16 | // IP_BLACK_LIST(1), 17 | 18 | // IP_WHITE_LIST(2), 19 | 20 | ; 21 | 22 | 23 | @EnumValue 24 | private final Integer value; 25 | 26 | SpecialListType(Integer value) { 27 | this.value = value; 28 | } 29 | 30 | @Override 31 | public Integer getValue() { 32 | return value; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/enums/ValueEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.enums; 2 | 3 | import org.springframework.util.Assert; 4 | 5 | import java.util.stream.Stream; 6 | 7 | /** 8 | * Value Enum 9 | */ 10 | public interface ValueEnum { 11 | 12 | static > E valueToEnum(Class enumType, V value) { 13 | Assert.notNull(enumType, "enum type must not be null"); 14 | Assert.isTrue(enumType.isEnum(), "type must be an enum type"); 15 | Assert.notNull(value, "value must not be null"); 16 | 17 | return Stream.of(enumType.getEnumConstants()) 18 | .filter(item -> item.getValue().equals(value)) 19 | .findFirst() 20 | .orElseThrow(() -> new IllegalArgumentException("unknown value: " + value)); 21 | } 22 | 23 | V getValue(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/AttachmentSearchParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * Attachment Search Param 13 | * 14 | * @author Myles Yang 15 | */ 16 | @EqualsAndHashCode(callSuper = true) 17 | @Data 18 | @Accessors(chain = true) 19 | @ApiModel("附件搜索参数") 20 | public class AttachmentSearchParam extends BasePageParam { 21 | 22 | @ApiModelProperty("附件名") 23 | private String name; 24 | 25 | @ApiModelProperty("附件类型") 26 | private String mediaType; 27 | 28 | @ApiModelProperty("附件分组") 29 | private String team; 30 | 31 | @ApiModelProperty("附件分组模糊查询字段") 32 | private String likeTeam; 33 | 34 | private LocalDateTime createTime; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/BasePageParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | import javax.validation.constraints.Max; 9 | import javax.validation.constraints.Min; 10 | 11 | /** 12 | * Base Page Param 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("基础分页参数") 19 | public class BasePageParam { 20 | 21 | @Min(1) 22 | @ApiModelProperty("当前页") 23 | private long current; 24 | 25 | @Min(1) 26 | @Max(50) 27 | @ApiModelProperty("每页数量") 28 | private long pageSize; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/BlogSearchParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | import xyz.snwjas.blog.model.enums.BlogStatus; 9 | 10 | /** 11 | * Blog Search Param 12 | * 13 | * @author Myles Yang 14 | */ 15 | @EqualsAndHashCode(callSuper = true) 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("博客搜索参数") 19 | public class BlogSearchParam extends BasePageParam { 20 | 21 | @ApiModelProperty("博客文章标题") 22 | private String title; 23 | 24 | @ApiModelProperty("博客分类ID") 25 | private Integer categoryId; 26 | 27 | @ApiModelProperty("博客标签ID") 28 | private Integer tagId; 29 | 30 | @ApiModelProperty("博客状态") 31 | private BlogStatus status; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/CommentSearchParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | import xyz.snwjas.blog.model.enums.CommentStatus; 9 | 10 | /** 11 | * Common Search Param 12 | * 13 | * @author Myles Yang 14 | */ 15 | @EqualsAndHashCode(callSuper = true) 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("评论搜索参数") 19 | public class CommentSearchParam extends BasePageParam { 20 | 21 | @ApiModelProperty("评论者昵称") 22 | private String author; 23 | 24 | @ApiModelProperty("评论者昵称") 25 | private String content; 26 | 27 | @ApiModelProperty("评论者邮箱地址") 28 | private String email; 29 | 30 | @ApiModelProperty("关键字:author || content || email") 31 | private String keyword; // author || content || email 32 | 33 | @ApiModelProperty("评论所属博客文章ID") 34 | private Integer blogId; 35 | 36 | @ApiModelProperty("评论状态") 37 | private CommentStatus status; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/LinkSearchParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | 9 | /** 10 | * Link Search Param 11 | * 12 | * @author Myles Yang 13 | */ 14 | @EqualsAndHashCode(callSuper = true) 15 | @Data 16 | @Accessors(chain = true) 17 | @ApiModel("友链搜索参数") 18 | public class LinkSearchParam extends BasePageParam { 19 | 20 | @ApiModelProperty("友链名称") 21 | private String name; 22 | 23 | @ApiModelProperty("友链url") 24 | private String url; 25 | 26 | @ApiModelProperty("关键词:名称或url") 27 | private String keyword; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/ListParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.experimental.Delegate; 6 | 7 | import javax.validation.Valid; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @ApiModel("列表参数") 12 | public class ListParam implements List { 13 | 14 | @Delegate // @Delegate是lombok注解 15 | @Valid // 一定要加@Valid注解 16 | @ApiModelProperty("列表数据") 17 | public List list = new ArrayList<>(); 18 | 19 | // 一定要记得重写toString方法 20 | @Override 21 | public String toString() { 22 | return list.toString(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/LogSearchParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | import xyz.snwjas.blog.model.enums.LogType; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Log Search Param 14 | * 15 | * @author Myles Yang 16 | */ 17 | @EqualsAndHashCode(callSuper = true) 18 | @Data 19 | @Accessors(chain = true) 20 | @ApiModel("日志搜索参数") 21 | public class LogSearchParam extends BasePageParam { 22 | 23 | @ApiModelProperty("日志类型") 24 | private List types; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/SpeciallistSearchParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | import xyz.snwjas.blog.model.enums.SpecialListType; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Special List Search Param 14 | * 15 | * @author Myles Yang 16 | */ 17 | @EqualsAndHashCode(callSuper = true) 18 | @Data 19 | @Accessors(chain = true) 20 | @ApiModel("特殊清单搜索参数") 21 | public class SpeciallistSearchParam extends BasePageParam { 22 | 23 | @ApiModelProperty("类型") 24 | private List types; 25 | 26 | @ApiModelProperty("内容") 27 | private String content; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/params/UpdatePasswordParam.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.params; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import org.hibernate.validator.constraints.Length; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | 10 | /** 11 | * Update Password Param 12 | * 13 | * @author Myles Yang 14 | */ 15 | @Data 16 | @ApiModel("密码更新参数") 17 | public class UpdatePasswordParam { 18 | 19 | @NotBlank 20 | @Length(max = 63) 21 | @ApiModelProperty("原密码") 22 | private String oldPassword; 23 | 24 | @NotBlank 25 | @Length(max = 63) 26 | @ApiModelProperty("新密码") 27 | private String newPassword; 28 | 29 | @NotBlank 30 | @Length(max = 63) 31 | @ApiModelProperty("确认的新密码") 32 | private String confirmNewPassword; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/AttachmentVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import org.hibernate.validator.constraints.Length; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | import xyz.snwjas.blog.model.base.ValidGroupType.Save; 10 | import xyz.snwjas.blog.model.base.ValidGroupType.Update; 11 | 12 | import javax.validation.constraints.Min; 13 | import javax.validation.constraints.NotBlank; 14 | import javax.validation.constraints.NotNull; 15 | import java.io.Serializable; 16 | import java.time.LocalDateTime; 17 | 18 | /** 19 | * Attachment VO 20 | * 21 | * @author Myles Yang 22 | */ 23 | @Data 24 | @Accessors(chain = true) 25 | @ApiModel("附件视图对象") 26 | public class AttachmentVO implements BeanConvert, Serializable { 27 | 28 | private static final long serialVersionUID = 2383027373825151464L; 29 | 30 | @NotNull(groups = {Update.class}) 31 | @Min(value = 1, groups = {Update.class}) 32 | @ApiModelProperty("附件ID") 33 | private Integer id; 34 | 35 | @NotBlank(groups = {Save.class, Update.class}) 36 | @Length(max = 255, groups = {Save.class, Update.class}) 37 | @ApiModelProperty("附件名") 38 | private String name; 39 | 40 | @ApiModelProperty("附件大小(单位:字节)") 41 | private Long size; 42 | 43 | @ApiModelProperty("附件路径") 44 | private String path; 45 | 46 | @ApiModelProperty("附件资源类型") 47 | private String mediaType; 48 | 49 | @ApiModelProperty("附件缩略图路径") 50 | private String thumbPath; 51 | 52 | @ApiModelProperty(value = "文件分组") 53 | private String team; 54 | 55 | @ApiModelProperty("附件为图片时的宽度") 56 | private Integer width; 57 | 58 | @ApiModelProperty("附件为图片时的高度") 59 | private Integer height; 60 | 61 | private LocalDateTime createTime; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/BlogArchiveVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import xyz.snwjas.blog.model.base.BeanConvert; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * 博客归档 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Data 17 | @ApiModel("博客归档视图对象") 18 | public class BlogArchiveVO implements BeanConvert, Serializable { 19 | 20 | private static final long serialVersionUID = 5910901865665778938L; 21 | 22 | @ApiModelProperty("博客文章ID") 23 | private Integer id; 24 | 25 | @ApiModelProperty("博客文章标题") 26 | private String title; 27 | 28 | @ApiModelProperty("博客文章URL") 29 | private String url; 30 | 31 | private LocalDateTime createTime; 32 | 33 | private LocalDateTime updateTime; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/BlogDetailVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * 博客列表View Object 14 | * 15 | * @author Myles Yang 16 | */ 17 | @EqualsAndHashCode(callSuper = true) 18 | @Data 19 | @Accessors(chain = true) 20 | @ApiModel("详细的博客文章视图对象") 21 | public class BlogDetailVO extends BlogSimpleVO implements BeanConvert, Serializable { 22 | 23 | private static final long serialVersionUID = 4692344576356025171L; 24 | 25 | @ApiModelProperty("源格式文本(Markdown)") 26 | private String originalContent; 27 | 28 | @ApiModelProperty("格式化文本(HTML)") 29 | private String formatContent; 30 | 31 | @ApiModelProperty("点赞数量") 32 | private Integer likes; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/BlogSelectVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import xyz.snwjas.blog.model.base.BeanConvert; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * Blog Select VO 13 | * 评论中选择文章搜索 14 | * 15 | * @author Myles Yang 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @ApiModel("评论中选择文章搜索的视图对象") 20 | public class BlogSelectVO implements BeanConvert, Serializable { 21 | 22 | private static final long serialVersionUID = 9072913105080249037L; 23 | 24 | @ApiModelProperty("文章ID") 25 | private Integer id; 26 | 27 | @ApiModelProperty("文章标题") 28 | private String title; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/BlogSimpleVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import org.hibernate.validator.constraints.Length; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | import xyz.snwjas.blog.model.base.ValidGroupType.Save; 10 | import xyz.snwjas.blog.model.base.ValidGroupType.Update; 11 | import xyz.snwjas.blog.model.enums.BlogCommentAllowStatus; 12 | import xyz.snwjas.blog.model.enums.BlogStatus; 13 | 14 | import javax.validation.constraints.Min; 15 | import javax.validation.constraints.NotNull; 16 | import java.io.Serializable; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | 20 | /** 21 | * 博客列表View Object 22 | * 23 | * @author Myles Yang 24 | */ 25 | @Data 26 | @Accessors(chain = true) 27 | @ApiModel("简单的博客文章视图对象") 28 | public class BlogSimpleVO implements BeanConvert, Serializable { 29 | 30 | private static final long serialVersionUID = 6349521681640492470L; 31 | 32 | @NotNull(groups = Update.class, message = "博客id不能为空") 33 | @Min(value = 1, groups = Update.class) 34 | @ApiModelProperty("文章ID") 35 | private Integer id; 36 | 37 | @Length(max = 255, groups = {Save.class, Update.class}) 38 | @ApiModelProperty("文章标题") 39 | private String title; 40 | 41 | @Length(max = 255, groups = {Save.class, Update.class}) 42 | @ApiModelProperty("文章访问URL") 43 | private String url; 44 | 45 | @Length(max = 511, groups = {Save.class, Update.class}) 46 | @ApiModelProperty("文章概要") 47 | private String summary; 48 | 49 | @Length(max = 1023, groups = {Save.class, Update.class}) 50 | @ApiModelProperty("文章缩略图") 51 | private String thumbnail; 52 | 53 | @ApiModelProperty("文章置顶数值(数值越大越靠前)") 54 | private Integer topRank; 55 | 56 | @ApiModelProperty("文章访问数量") 57 | private Integer visits; 58 | 59 | @ApiModelProperty("文章状态") 60 | private BlogStatus status; 61 | 62 | @ApiModelProperty("文章分类") 63 | private CategoryVO category; 64 | 65 | @ApiModelProperty("文章是否允许评论") 66 | private BlogCommentAllowStatus allowComment; 67 | 68 | private LocalDateTime createTime; 69 | 70 | private LocalDateTime updateTime; 71 | 72 | @ApiModelProperty("评论数量") 73 | private Integer commentCount; 74 | 75 | @ApiModelProperty("文章标签列表") 76 | private List tags; 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/CategoryVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import org.hibernate.validator.constraints.Length; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | import xyz.snwjas.blog.model.base.ValidGroupType.Save; 10 | import xyz.snwjas.blog.model.base.ValidGroupType.Update; 11 | 12 | import javax.validation.constraints.Min; 13 | import javax.validation.constraints.NotBlank; 14 | import javax.validation.constraints.NotNull; 15 | import java.io.Serializable; 16 | 17 | /** 18 | * Category VO 19 | * 20 | * @author Myles Yang 21 | */ 22 | @Data 23 | @Accessors(chain = true) 24 | @ApiModel("分类视图对象") 25 | public class CategoryVO implements BeanConvert, Serializable { 26 | 27 | private static final long serialVersionUID = 7673727208979429235L; 28 | 29 | @NotNull(groups = {Update.class}) 30 | @Min(value = 1, groups = {Update.class}) 31 | @ApiModelProperty("分类ID") 32 | private Integer id; 33 | 34 | @NotBlank(message = "分类名不能为空", groups = {Save.class, Update.class}) 35 | @Length(min = 1, max = 63, message = "分类名长度应介于1至63之间", groups = {Save.class, Update.class}) 36 | @ApiModelProperty("分类名") 37 | private String name; 38 | 39 | @Length(max = 127, message = "分类描述长度应小于127", groups = {Save.class, Update.class}) 40 | @ApiModelProperty("分类描述") 41 | private String description; 42 | 43 | @ApiModelProperty("该分类下的博客数量") 44 | private Integer blogCount; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/CommentDetailVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.ToString; 8 | import lombok.experimental.Accessors; 9 | import org.hibernate.validator.constraints.Length; 10 | import xyz.snwjas.blog.model.enums.CommentStatus; 11 | 12 | import javax.validation.constraints.Email; 13 | import javax.validation.constraints.NotEmpty; 14 | 15 | /** 16 | * Comment VO 17 | * 18 | * @author Myles Yang 19 | */ 20 | @EqualsAndHashCode(callSuper = true) 21 | @Data 22 | @ToString(callSuper = true) 23 | @Accessors(chain = true) 24 | @ApiModel("详细的评论视图对象") 25 | public class CommentDetailVO extends CommentSimpleVO { 26 | 27 | private static final long serialVersionUID = 1080754350404773083L; 28 | 29 | @NotEmpty(groups = {Guest.class}, message = "邮箱不能为空") 30 | @Email(groups = {Guest.class}, message = "邮箱格式错误") 31 | @Length(max = 127, groups = {Admin.class, Guest.class}, message = "邮箱长度不能超过127个字符") 32 | @ApiModelProperty("评论者邮件地址") 33 | private String email; 34 | 35 | @ApiModelProperty("评论者IP地址") 36 | private String ipAddress; 37 | 38 | @ApiModelProperty("评论状态") 39 | private CommentStatus status; 40 | 41 | private String blogTitle; 42 | 43 | private String blogUrl; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/CommentSimpleVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import org.hibernate.validator.constraints.Length; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | 10 | import javax.validation.constraints.Min; 11 | import javax.validation.constraints.NotEmpty; 12 | import javax.validation.constraints.NotNull; 13 | import java.io.Serializable; 14 | import java.time.LocalDateTime; 15 | import java.util.List; 16 | 17 | /** 18 | * Comment VO 19 | * 20 | * @author Myles Yang 21 | */ 22 | @Data 23 | @Accessors(chain = true) 24 | @ApiModel("简单的评论视图对象") 25 | public class CommentSimpleVO implements BeanConvert, Serializable { 26 | 27 | private static final long serialVersionUID = -7923533144012981489L; 28 | 29 | @ApiModelProperty("评论ID") 30 | private Integer id; 31 | 32 | @NotEmpty(groups = {Admin.class, Guest.class}, message = "评论内容不能为空") 33 | @Length(max = 1023, groups = {Admin.class, Guest.class}, message = "评论内容长度请控制在1023个字符") 34 | @ApiModelProperty("评论内容") 35 | private String content; 36 | 37 | @NotEmpty(groups = {Guest.class}, message = "昵称不能为空") 38 | @Length(max = 63, groups = {Admin.class, Guest.class}, message = "昵称长度不能超过63个字符") 39 | @ApiModelProperty("评论者昵称") 40 | private String author; 41 | 42 | @Min(value = 0, groups = {Admin.class, Guest.class}) 43 | @ApiModelProperty("父评论ID") 44 | private Integer parentId; 45 | 46 | @NotNull(groups = {Admin.class, Guest.class}, message = "未知博客的评论") 47 | @Min(value = 1, groups = {Admin.class, Guest.class}, message = "未知博客的评论") 48 | @ApiModelProperty("评论所属博客文章ID") 49 | private Integer blogId; 50 | 51 | @ApiModelProperty("评论者的用户代理") 52 | private String userAgent; 53 | 54 | @ApiModelProperty("评论是否是博主发表的") 55 | private Boolean isAdmin; 56 | 57 | @ApiModelProperty(value = "评论作者头像") 58 | private String avatar; 59 | 60 | private LocalDateTime createTime; 61 | 62 | @ApiModelProperty("子评论数量") 63 | private Integer childrenCount; 64 | 65 | @ApiModelProperty("子评论") 66 | private List children; 67 | 68 | // 管理员校验 69 | public interface Admin { 70 | } 71 | 72 | // 游客校验 73 | public interface Guest { 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/LinkVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import org.hibernate.validator.constraints.Length; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | import xyz.snwjas.blog.model.base.ValidGroupType; 10 | 11 | import javax.validation.constraints.Min; 12 | import javax.validation.constraints.NotBlank; 13 | import javax.validation.constraints.NotNull; 14 | import java.io.Serializable; 15 | 16 | /** 17 | * Link VO 18 | * 19 | * @author Myles Yang 20 | */ 21 | @Data 22 | @Accessors(chain = true) 23 | @ApiModel("友链视图对象") 24 | public class LinkVO implements BeanConvert, Serializable { 25 | 26 | private static final long serialVersionUID = 4860407888308055369L; 27 | 28 | @NotNull(groups = {ValidGroupType.Update.class}) 29 | @Min(value = 1, groups = {ValidGroupType.Update.class}) 30 | @ApiModelProperty("友链ID") 31 | private Integer id; 32 | 33 | @NotBlank(groups = {ValidGroupType.Save.class, ValidGroupType.Update.class}, message = "友链名称不能为空") 34 | @Length(max = 127, groups = {ValidGroupType.Save.class, ValidGroupType.Update.class}) 35 | @ApiModelProperty("友链名") 36 | private String name; 37 | 38 | @NotBlank(groups = {ValidGroupType.Save.class, ValidGroupType.Update.class}, message = "友链地址不能为空") 39 | @Length(max = 255, groups = {ValidGroupType.Save.class, ValidGroupType.Update.class}) 40 | @ApiModelProperty("友链地址") 41 | private String url; 42 | 43 | @Length(max = 1023) 44 | @ApiModelProperty("友链Logo地址") 45 | private String logo; 46 | 47 | @ApiModelProperty("友链排行(值越大越靠前)") 48 | private Integer topRank; 49 | 50 | @Length(max = 512) 51 | @ApiModelProperty("友链描述") 52 | private String description; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/LogVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import xyz.snwjas.blog.model.base.BeanConvert; 8 | import xyz.snwjas.blog.model.enums.LogType; 9 | 10 | import java.io.Serializable; 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | * Log VO 15 | * 16 | * @author Myles Yang 17 | */ 18 | @Data 19 | @Accessors(chain = true) 20 | @ApiModel("日志视图对象") 21 | public class LogVO implements BeanConvert, Serializable { 22 | 23 | private static final long serialVersionUID = -4592668651066079493L; 24 | 25 | @ApiModelProperty("日志ID") 26 | private Integer id; 27 | 28 | @ApiModelProperty("操作内容") 29 | private String content; 30 | 31 | @ApiModelProperty("操作类型") 32 | private LogType type; 33 | 34 | @ApiModelProperty("操作人ipv4地址") 35 | private String ipAddress; 36 | 37 | @ApiModelProperty("操作时间") 38 | private LocalDateTime createTime; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/OptionVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import xyz.snwjas.blog.model.base.BeanConvert; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * Option VO 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("设置选项视图对象") 19 | public class OptionVO implements BeanConvert, Serializable { 20 | 21 | private static final long serialVersionUID = -2070361214962849320L; 22 | 23 | @ApiModelProperty("选项ID") 24 | private Integer id; 25 | 26 | @ApiModelProperty("选项键") 27 | private String optionKey; 28 | 29 | @ApiModelProperty("选项值") 30 | private String optionValue; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/SpeciallistVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.experimental.Accessors; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | import xyz.snwjas.blog.model.enums.SpecialListType; 10 | 11 | import java.io.Serializable; 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * Special List VO 16 | * 17 | * @author Myles Yang 18 | */ 19 | @NoArgsConstructor 20 | @Data 21 | @Accessors(chain = true) 22 | @ApiModel("特殊清单视图对象") 23 | public class SpeciallistVO implements BeanConvert, Serializable { 24 | 25 | private static final long serialVersionUID = 1258610051667767051L; 26 | 27 | @ApiModelProperty("ID") 28 | private Integer id; 29 | 30 | @ApiModelProperty("类型") 31 | private SpecialListType type; 32 | 33 | @ApiModelProperty("内容") 34 | private String content; 35 | 36 | @ApiModelProperty("创建时间") 37 | private LocalDateTime createTime; 38 | 39 | public SpeciallistVO(SpecialListType type, String content) { 40 | this.type = type; 41 | this.content = content; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/StatisticsBasicVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | /** 9 | * 博客数据统计View Object 10 | * 11 | * @author Myles Yang 12 | */ 13 | 14 | @Data 15 | @Accessors(chain = true) 16 | public class StatisticsBasicVO { 17 | 18 | private static final long serialVersionUID = 4310667613112942914L; 19 | 20 | private Integer blogCount; 21 | 22 | private Integer commentCount; 23 | 24 | private Integer categoryCount; 25 | 26 | private Integer tagCount; 27 | 28 | private Integer linkCount; 29 | 30 | private Integer establishDaysCount; 31 | 32 | // @JsonFormat(pattern = "yyyy-MM-dd") 33 | private LocalDateTime birthday; 34 | 35 | private Integer blogTotalVisits; 36 | 37 | private Integer webTotalVisits; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/StatisticsReportVo.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | import xyz.snwjas.blog.model.base.BeanConvert; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * 博客每天的统计数据 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | public class StatisticsReportVo implements BeanConvert, Serializable { 19 | 20 | private static final long serialVersionUID = -1362266664886856765L; 21 | 22 | private Integer webVisitCount; 23 | 24 | private Integer blogVisitCount; 25 | 26 | private Integer commentCount; 27 | 28 | @JsonFormat(pattern = "yyyy-MM-dd") 29 | private LocalDateTime date; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/TagVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | import org.hibernate.validator.constraints.Length; 8 | import xyz.snwjas.blog.model.base.BeanConvert; 9 | import xyz.snwjas.blog.model.base.ValidGroupType.Save; 10 | import xyz.snwjas.blog.model.base.ValidGroupType.Update; 11 | 12 | import javax.validation.constraints.Min; 13 | import javax.validation.constraints.NotBlank; 14 | import javax.validation.constraints.NotNull; 15 | import java.io.Serializable; 16 | 17 | /** 18 | * Tag VO 19 | * 20 | * @author Myles Yang 21 | */ 22 | @Data 23 | @Accessors(chain = true) 24 | @ApiModel("标签视图对象") 25 | public class TagVO implements BeanConvert, Serializable { 26 | 27 | private static final long serialVersionUID = -2783711626542947755L; 28 | 29 | @NotNull(groups = {Update.class}) 30 | @Min(value = 1, groups = {Update.class}) 31 | @ApiModelProperty("标签ID") 32 | private Integer id; 33 | 34 | @NotBlank(message = "标签名不能为空", groups = {Save.class, Update.class}) 35 | @Length(min = 1, max = 63, message = "标签名长度应介于1至63之间", groups = {Save.class, Update.class}) 36 | @ApiModelProperty("标签名") 37 | private String name; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/model/vo/UserVO.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.model.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import org.hibernate.validator.constraints.Length; 7 | import xyz.snwjas.blog.model.base.BeanConvert; 8 | 9 | import javax.validation.constraints.Email; 10 | import java.io.Serializable; 11 | 12 | /** 13 | * User VO 14 | * 15 | * @author Myles Yang 16 | */ 17 | @Data 18 | @ApiModel("用户视图对象") 19 | public class UserVO implements BeanConvert, Serializable { 20 | 21 | private static final long serialVersionUID = -2053049610535340848L; 22 | 23 | @Length(max = 63) 24 | @ApiModelProperty("用户名") 25 | private String username; 26 | 27 | @Length(max = 127) 28 | @ApiModelProperty("用户昵称") 29 | private String nickname; 30 | 31 | @Email 32 | @Length(max = 127) 33 | @ApiModelProperty("用户邮箱地址") 34 | private String email; 35 | 36 | @Length(max = 1023) 37 | @ApiModelProperty("用户头像地址") 38 | private String avatar; 39 | 40 | @Length(max = 1023) 41 | @ApiModelProperty("用户描述") 42 | private String description; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/schedule/StatisticTask.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.schedule; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.scheduling.annotation.Async; 6 | import org.springframework.scheduling.annotation.Scheduled; 7 | import org.springframework.stereotype.Component; 8 | import xyz.snwjas.blog.service.StatisticsService; 9 | 10 | /** 11 | * 数据统计定时任务 12 | * 13 | * @author Myles Yang 14 | */ 15 | @Component 16 | @Slf4j 17 | public class StatisticTask { 18 | 19 | @Autowired 20 | private StatisticsService statisticsService; 21 | 22 | /* @PostConstruct 23 | public void run() { 24 | autoStat(); 25 | }*/ 26 | 27 | // 每天零点统计信息 28 | @Async("executor") 29 | @Scheduled(cron = "1 0 0 */1 * ?") 30 | public void autoStat() { 31 | statisticsService.statisticsDaily(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/AttachmentService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import org.springframework.web.multipart.MultipartFile; 5 | import xyz.snwjas.blog.model.entity.AttachmentEntity; 6 | import xyz.snwjas.blog.model.params.AttachmentSearchParam; 7 | import xyz.snwjas.blog.model.vo.AttachmentVO; 8 | import xyz.snwjas.blog.model.PageResult; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Attachment Service 14 | * 15 | * @author Myles Yang 16 | */ 17 | public interface AttachmentService { 18 | 19 | /** 20 | * 添加附件 21 | */ 22 | AttachmentVO add(MultipartFile file, String team); 23 | 24 | /** 25 | * 修改附件名 26 | */ 27 | int updateNameById(int attachmentId, String attachmentName); 28 | 29 | /** 30 | * 删除附件 31 | */ 32 | int deleteById(int attachmentId); 33 | 34 | /** 35 | * 批量删除附件 36 | */ 37 | int deleteById(List attachmentIds); 38 | 39 | /** 40 | * 更新附件分组 41 | */ 42 | int updateTeam(List attachmentIds, String team); 43 | 44 | /** 45 | * 获取所有的文件类型 46 | */ 47 | List listAllMediaTypes(); 48 | 49 | /** 50 | * 获取所有分组 51 | */ 52 | List listAllTeams(); 53 | 54 | IPage pageBy(AttachmentSearchParam param); 55 | 56 | PageResult covertToPageResult(IPage page); 57 | 58 | AttachmentVO covertToVO(AttachmentEntity attachmentEntity); 59 | 60 | List covertToListVO(List attachmentEntityList); 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/BlogService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import xyz.snwjas.blog.model.entity.BlogEntity; 5 | import xyz.snwjas.blog.model.enums.BlogStatus; 6 | import xyz.snwjas.blog.model.params.BlogSearchParam; 7 | import xyz.snwjas.blog.model.vo.BlogArchiveVO; 8 | import xyz.snwjas.blog.model.vo.BlogDetailVO; 9 | import xyz.snwjas.blog.model.vo.BlogSelectVO; 10 | import xyz.snwjas.blog.model.vo.BlogSimpleVO; 11 | import xyz.snwjas.blog.model.PageResult; 12 | 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * Blog Service 18 | * 19 | * @author Myles Yang 20 | */ 21 | public interface BlogService { 22 | 23 | /** 24 | * 获取已发表的博客数量 25 | * 26 | * @param status 为null则查询全部数量 27 | */ 28 | int getCount(BlogStatus status); 29 | 30 | /** 31 | * 根据分类获取博客数量 32 | * 33 | * @param status 为null则查询对应分类的全部数量 34 | */ 35 | int getCountByCategoryId(int categoryId, BlogStatus status); 36 | 37 | /** 38 | * 获取博客的总访问量 39 | */ 40 | int getTotalVisits(); 41 | 42 | /** 43 | * 根据条件获取博客分页信息(不包括originalContent与formatContent) 44 | * 默认升序分页 45 | */ 46 | IPage pageBy(BlogSearchParam param); 47 | 48 | /** 49 | * 列出所有文章标题及其Id 50 | */ 51 | List listAllTitle(); 52 | 53 | /** 54 | * 列出博客归档,返回对应年份的归档 55 | */ 56 | Map> listArchive(); 57 | 58 | /** 59 | * 根据博客id获取博客的详细信息 60 | */ 61 | BlogDetailVO getDetailById(int blogId); 62 | 63 | /** 64 | * 根据博客id获取博客的简单信息(不查询博客内容) 65 | */ 66 | BlogSimpleVO getSimpleById(int blogId); 67 | 68 | /** 69 | * 根据博客url获取博客的详细信息 70 | */ 71 | BlogDetailVO getByUrl(String blogUrl); 72 | 73 | /** 74 | * 更新博客信息 75 | */ 76 | int update(BlogDetailVO vo); 77 | 78 | /** 79 | * 删除博客 80 | */ 81 | int deleteById(int blogId); 82 | 83 | /** 84 | * 添加博客 85 | */ 86 | int add(BlogDetailVO vo); 87 | 88 | /** 89 | * 点赞博客 90 | * 91 | * @return -1 取消点赞, 1 点赞成功,0 博客不存在 92 | */ 93 | int like(int blogId); 94 | 95 | /** 96 | * 博客是否可以评论 97 | */ 98 | boolean canComment(int blogId); 99 | 100 | /** 101 | * 根据博客id判断博客是否存在 102 | */ 103 | boolean isExist(int blogId); 104 | 105 | /** 106 | * 根据博客路径判断博客是否存在 107 | */ 108 | boolean isExist(String url); 109 | 110 | /** 111 | * 根据博客路径判断博客是否存在 112 | */ 113 | boolean isExist(int blogId, String url); 114 | 115 | /** 116 | * BlogEntity to BlogSimpleVO 117 | */ 118 | BlogSimpleVO covertToSimpleVO(BlogEntity blogEntity); 119 | 120 | /** 121 | * BlogEntity to BlogDetailVO 122 | */ 123 | BlogDetailVO covertToDetailVO(BlogEntity blogEntity); 124 | 125 | /** 126 | * List to List 127 | */ 128 | List covertToListSimpleVO(List blogEntityList); 129 | 130 | /** 131 | * IPage to PageResult 132 | */ 133 | PageResult covertToPageResult(IPage blogPage); 134 | 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import xyz.snwjas.blog.model.entity.CategoryEntity; 5 | import xyz.snwjas.blog.model.params.BasePageParam; 6 | import xyz.snwjas.blog.model.vo.CategoryVO; 7 | import xyz.snwjas.blog.model.PageResult; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Category Service 13 | * 14 | * @author Myles Yang 15 | */ 16 | public interface CategoryService { 17 | 18 | /** 19 | * 获取分类数量 20 | */ 21 | int getCount(); 22 | 23 | /** 24 | * 列出已使用的博客分类名 25 | */ 26 | List listUsedCategory(); 27 | 28 | /** 29 | * 获取所有分类 30 | */ 31 | List listAllCategory(); 32 | 33 | /** 34 | * 分页查询分类 35 | */ 36 | IPage pageBy(BasePageParam param); 37 | 38 | /** 39 | * 根据分类id回去分类信息 40 | */ 41 | CategoryVO getById(int categoryId); 42 | 43 | /** 44 | * 根据分类名回去分类信息 45 | */ 46 | CategoryVO getByName(String name); 47 | 48 | /** 49 | * 添加分类 50 | * 51 | * @return 分类id, >0:添加成功; <0:已存在分类的id负值 52 | */ 53 | int add(CategoryVO categoryVO); 54 | 55 | /** 56 | * 更新分类信息 57 | * 58 | * @return >0:更新成功 59 | */ 60 | int update(CategoryVO categoryVO); 61 | 62 | /** 63 | * 删除分类 64 | */ 65 | int deleteById(int categoryId); 66 | 67 | CategoryVO covertToVO(CategoryEntity categoryEntity); 68 | 69 | List covertToListVO(List categoryEntityList); 70 | 71 | PageResult covertToPageResult(IPage categoryPage); 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import org.springframework.lang.NonNull; 5 | import xyz.snwjas.blog.model.PageResult; 6 | import xyz.snwjas.blog.model.entity.CommentEntity; 7 | import xyz.snwjas.blog.model.enums.CommentStatus; 8 | import xyz.snwjas.blog.model.params.BasePageParam; 9 | import xyz.snwjas.blog.model.params.CommentSearchParam; 10 | import xyz.snwjas.blog.model.vo.CommentDetailVO; 11 | import xyz.snwjas.blog.model.vo.CommentSimpleVO; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * Comment Service 17 | * 18 | * @author Myles Yang 19 | */ 20 | public interface CommentService { 21 | 22 | /** 23 | * 获取所有的评论的数量 24 | */ 25 | int getCountByStatus(CommentStatus status); 26 | 27 | /** 28 | * 获取博客的评论数量 29 | */ 30 | int getCountByBlogIdAndStatus(int blogId, CommentStatus status); 31 | 32 | /** 33 | * 获取子评论数量 34 | */ 35 | int getCountByParentIdAndStatus(int parentId, CommentStatus status); 36 | 37 | /** 38 | * 改变评论状态 39 | */ 40 | int changeStatus(int commentId, CommentStatus status); 41 | 42 | /** 43 | * 批量改变评论状态 44 | * 45 | * @return 成功更新状态的数量 46 | */ 47 | int changeStatus(List commentIds, CommentStatus status); 48 | 49 | /** 50 | * 删除评论 51 | */ 52 | int deleteById(int commentId); 53 | 54 | /** 55 | * 批量删除评论 56 | * 57 | * @return 成功删除的数量 58 | */ 59 | int deleteByIds(@NonNull List commentIds); 60 | 61 | /** 62 | * 根据博客id删除评论 63 | */ 64 | int deleteByBlogId(int blogId); 65 | 66 | /** 67 | * 回复评论 68 | * 69 | * @return > 0 评论id 70 | * 0 不允许评论 71 | * -1 文章不存在 72 | */ 73 | int reply(CommentDetailVO vo); 74 | 75 | /** 76 | * 根据条件搜索评论 77 | */ 78 | IPage pageBy(CommentSearchParam param); 79 | 80 | /** 81 | * 分页获取前台某层评论 82 | * 83 | * @param parentId 为 0 为顶层 84 | */ 85 | IPage pageBy(int blogId, int parentId, BasePageParam param); 86 | 87 | /** 88 | * 从顶层往下递归获取所有子评论 89 | */ 90 | IPage pageBy(int blogId, BasePageParam param); 91 | 92 | CommentSimpleVO covertToSimpleVO(CommentEntity commentEntity); 93 | 94 | CommentDetailVO covertToDetailVO(CommentEntity commentEntity); 95 | 96 | PageResult covertToDetailPageResult(IPage page); 97 | 98 | PageResult covertToSimplePageResult(IPage page); 99 | 100 | /** 101 | * 从顶层往下递归获取所有子评论 102 | */ 103 | PageResult covertToSimplePageResultByRecursively(IPage page); 104 | 105 | List covertToListDetailVO(List commentEntityList); 106 | 107 | List covertToListSimpleVO(List commentEntityList); 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/LinkService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import xyz.snwjas.blog.model.entity.LinkEntity; 5 | import xyz.snwjas.blog.model.params.LinkSearchParam; 6 | import xyz.snwjas.blog.model.vo.LinkVO; 7 | import xyz.snwjas.blog.model.PageResult; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Link Service 13 | * 14 | * @author Myles Yang 15 | */ 16 | public interface LinkService { 17 | 18 | /** 19 | * 条件查询友链 20 | */ 21 | IPage pageBy(LinkSearchParam param); 22 | 23 | /** 24 | * 添加友链 25 | */ 26 | int add(LinkVO vo); 27 | 28 | /** 29 | * 根据id删除友链 30 | */ 31 | int deleteById(int linkId); 32 | 33 | /** 34 | * 更新友链 35 | */ 36 | int update(LinkVO vo); 37 | 38 | /** 39 | * 获取友链数目 40 | */ 41 | int getCount(); 42 | 43 | /** 44 | * 根据id获取友链 45 | */ 46 | LinkVO getById(int linkId); 47 | 48 | /** 49 | * 更新 URL Logo 解析器 50 | * @param parser 51 | * @return 52 | */ 53 | int updateLogoParser(String parser); 54 | 55 | LinkVO covertToVO(LinkEntity linkEntity); 56 | 57 | List covertToListVO(List linkEntityList); 58 | 59 | PageResult covertToPageResult(IPage page); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/LogService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import org.springframework.lang.NonNull; 5 | import xyz.snwjas.blog.model.PageResult; 6 | import xyz.snwjas.blog.model.entity.LogEntity; 7 | import xyz.snwjas.blog.model.params.LogSearchParam; 8 | import xyz.snwjas.blog.model.vo.LogVO; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Log Service 14 | * 15 | * @author Myles Yang 16 | */ 17 | public interface LogService { 18 | 19 | /** 20 | * 条件分页搜索日志,默认按id倒序查询 21 | */ 22 | IPage pageBy(LogSearchParam param); 23 | 24 | /** 25 | * 添加日志 26 | */ 27 | int add(LogVO logVO); 28 | 29 | /** 30 | * 删除日志 31 | */ 32 | int deleteById(int logId); 33 | 34 | /** 35 | * 批量删除日志 36 | */ 37 | int deleteByIds(@NonNull List ids); 38 | 39 | LogVO covertToVO(LogEntity logEntity); 40 | 41 | LogEntity covertToEntity(LogVO logVO); 42 | 43 | List covertToListVO(List logEntityList); 44 | 45 | PageResult covertToPageResult(IPage page); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/OptionsService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import xyz.snwjas.blog.model.entity.OptionsEntity; 4 | import xyz.snwjas.blog.model.vo.OptionVO; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * Options Service 10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface OptionsService { 14 | 15 | /** 16 | * 获取博客设置值 17 | * 18 | * @param optionKey key 19 | * @return {@link OptionVO} if key exists, else null 20 | */ 21 | OptionVO get(String optionKey); 22 | 23 | /** 24 | * 设置值,如果存在改变,不存在新建 25 | */ 26 | int setAnyway(String key, String value); 27 | 28 | /** 29 | * 设置值,如果键不存在 30 | */ 31 | int setIfAbsence(String key, String value); 32 | 33 | /** 34 | * 设置值,如果键存在 35 | */ 36 | int setIfPresent(String key, String value); 37 | 38 | /** 39 | * 获取所有键值 40 | */ 41 | Map listAll(); 42 | 43 | /** 44 | * 获取必要的键值 45 | */ 46 | Map listNecessary(); 47 | 48 | /** 49 | * 清空查询出来的 options Map缓存 50 | */ 51 | boolean resetOptionsCache(); 52 | 53 | /** 54 | * OptionsEntity to OptionVO 55 | */ 56 | OptionVO covertToVO(OptionsEntity optionsEntity); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/SpeciallistService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import org.springframework.lang.NonNull; 6 | import xyz.snwjas.blog.model.PageResult; 7 | import xyz.snwjas.blog.model.entity.SpeciallistEntity; 8 | import xyz.snwjas.blog.model.enums.SpecialListType; 9 | import xyz.snwjas.blog.model.params.SpeciallistSearchParam; 10 | import xyz.snwjas.blog.model.vo.SpeciallistVO; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Log Service 16 | * 17 | * @author Myles Yang 18 | */ 19 | public interface SpeciallistService extends IService { 20 | 21 | /** 22 | * 获取枚举的类型 23 | */ 24 | List listEnumType(); 25 | 26 | /** 27 | * 获取特定类型的所有记录 28 | */ 29 | List listAll(SpecialListType... types); 30 | 31 | /** 32 | * 条件分页搜索 33 | */ 34 | IPage pageBy(SpeciallistSearchParam param); 35 | 36 | /** 37 | * 添加清单 38 | */ 39 | int add(SpeciallistVO vo); 40 | 41 | /** 42 | * 批量导入 43 | */ 44 | boolean addBatch(SpecialListType type, @NonNull List contents); 45 | 46 | /** 47 | * 删除清单 48 | */ 49 | int deleteById(int id); 50 | 51 | /** 52 | * 批量删除清单 53 | */ 54 | int deleteByIds(@NonNull List ids); 55 | 56 | /** 57 | * 刷新缓存 58 | */ 59 | boolean refreshContext(SpecialListType type); 60 | 61 | SpeciallistVO covertToVO(SpeciallistEntity entity); 62 | 63 | List covertToListVO(List entityList); 64 | 65 | PageResult covertToPageResult(IPage page); 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/StatisticsService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import xyz.snwjas.blog.model.vo.StatisticsBasicVO; 4 | import xyz.snwjas.blog.model.vo.StatisticsReportVo; 5 | 6 | import java.time.LocalDateTime; 7 | import java.util.List; 8 | 9 | /** 10 | * Statistics Service 11 | * 12 | * @author Myles Yang 13 | */ 14 | public interface StatisticsService { 15 | 16 | /** 17 | * 获取基本的统计信息 18 | */ 19 | StatisticsBasicVO getCommonStatistics(); 20 | 21 | /** 22 | * 获取时间段 start - end 的每日统计信息 23 | */ 24 | List getDailyStatistics(LocalDateTime start, LocalDateTime end); 25 | 26 | /* 27 | * 统计昨日数据 28 | */ 29 | void statisticsDaily(); 30 | 31 | /** 32 | * 获取网站总访问量 33 | */ 34 | int getWebTotalVisit(); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/TagService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import xyz.snwjas.blog.model.entity.TagEntity; 4 | import xyz.snwjas.blog.model.vo.TagVO; 5 | 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | /** 10 | * Tag Service 11 | * 12 | * @author Myles Yang 13 | */ 14 | public interface TagService { 15 | 16 | /** 17 | * 获取所有的博客标签的数量 18 | */ 19 | int getCount(); 20 | 21 | /** 22 | * 获取某博客的标签 23 | */ 24 | List listBlogUsed(int blogId); 25 | 26 | /** 27 | * 获取所有标签 28 | */ 29 | List listAll(); 30 | 31 | /** 32 | * 获取已使用的标签 33 | */ 34 | List listUsed(); 35 | 36 | /** 37 | * 根据id获取单个标签 38 | */ 39 | TagVO getById(int tagId); 40 | 41 | /** 42 | * 根据标签id获取已使用该标签的博客id集合 43 | */ 44 | Set listBlogIds(int tagId); 45 | 46 | /** 47 | * 根据名称获取单个标签 48 | */ 49 | TagVO getByName(String name); 50 | 51 | /** 52 | * 添加标签 53 | * 54 | * @return >0:标签id;<0:已存在标签的id负值 55 | */ 56 | int add(TagVO tagVO); 57 | 58 | /** 59 | * 添加博客标签关联表记录 60 | */ 61 | int addBlogTag(int blogId, int tagId); 62 | 63 | 64 | /** 65 | * 更新标签 66 | * 67 | * @return 更新状态,>0 :更新成功 68 | */ 69 | int update(TagVO tagVO); 70 | 71 | /** 72 | * 删除标签 73 | */ 74 | int deleteById(int tagId); 75 | 76 | /** 77 | * 更新博客的标签 78 | * 79 | * @return 标签更新的数量 80 | */ 81 | int updateBlogUsed(int blogId, List tagVOList); 82 | 83 | /** 84 | * 删除博客标签 85 | */ 86 | int deleteBlogUsed(int blogId); 87 | 88 | /** 89 | * 删除博客标签关联表记录 90 | */ 91 | int deleteBlogTag(int blogId, int tagId); 92 | 93 | /** 94 | * TagEntity 转换成 TagVO 95 | */ 96 | TagVO covertToTagVO(TagEntity tagEntity); 97 | 98 | /** 99 | * List 转换成 List 100 | */ 101 | List covertToTagVOList(List tagEntityList); 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/UserService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service; 2 | 3 | import org.springframework.lang.NonNull; 4 | import org.springframework.security.core.Authentication; 5 | import xyz.snwjas.blog.model.vo.UserVO; 6 | import xyz.snwjas.blog.model.UserDetail; 7 | 8 | /** 9 | * User Service 10 | * 11 | * @author Myles Yang 12 | */ 13 | public interface UserService { 14 | 15 | /** 16 | * 根据用户名获取用户信息 17 | */ 18 | UserDetail getByUsername(@NonNull String username); 19 | 20 | /** 21 | * 根据用户id获取用户信息 22 | */ 23 | UserDetail getByUserId(int userId); 24 | 25 | /** 26 | * 根据id更新用户信息 27 | */ 28 | int updateProfile(int userId, UserVO userVO); 29 | 30 | /** 31 | * 更新密码 32 | */ 33 | int updatePassword(int userId, String newPassword); 34 | 35 | /** 36 | * 获取已登录的用户认证信息 37 | */ 38 | Authentication getAuth(); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/service/impl/LogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 6 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 7 | import org.springframework.lang.NonNull; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.util.CollectionUtils; 10 | import xyz.snwjas.blog.mapper.LogMapper; 11 | import xyz.snwjas.blog.model.PageResult; 12 | import xyz.snwjas.blog.model.entity.LogEntity; 13 | import xyz.snwjas.blog.model.params.LogSearchParam; 14 | import xyz.snwjas.blog.model.vo.LogVO; 15 | import xyz.snwjas.blog.service.LogService; 16 | import xyz.snwjas.blog.utils.IPUtils; 17 | 18 | import javax.annotation.Resource; 19 | import java.util.List; 20 | import java.util.Objects; 21 | import java.util.Set; 22 | import java.util.stream.Collectors; 23 | 24 | /** 25 | * Log Service Impl 26 | * 27 | * @author Myles Yang 28 | */ 29 | @Service 30 | public class LogServiceImpl implements LogService { 31 | 32 | @Resource 33 | private LogMapper logMapper; 34 | 35 | @Override 36 | public IPage pageBy(LogSearchParam param) { 37 | Page page = new Page<>(param.getCurrent(), param.getPageSize()); 38 | Wrapper wrapper = Wrappers.lambdaQuery(LogEntity.class) 39 | .in(!CollectionUtils.isEmpty(param.getTypes()), LogEntity::getType, param.getTypes()) 40 | .orderByDesc(LogEntity::getId); 41 | return logMapper.selectPage(page, wrapper); 42 | } 43 | 44 | @Override 45 | public int add(LogVO logVO) { 46 | LogEntity logEntity = covertToEntity(logVO); 47 | logMapper.insert(logEntity); 48 | return logEntity.getId(); 49 | } 50 | 51 | @Override 52 | public int deleteById(int logId) { 53 | return logMapper.deleteById(logId); 54 | } 55 | 56 | @Override 57 | public int deleteByIds(@NonNull List ids) { 58 | Set idSet = ids.stream() 59 | .filter(id -> Objects.nonNull(id) && id > 0) 60 | .collect(Collectors.toSet()); 61 | if (idSet.isEmpty()) { 62 | return 0; 63 | } 64 | return logMapper.deleteBatchIds(idSet); 65 | } 66 | 67 | @Override 68 | public LogVO covertToVO(LogEntity logEntity) { 69 | LogVO logVO = new LogVO().convertFrom(logEntity); 70 | Integer intIpv4 = logEntity.getIpAddress(); 71 | if (Objects.nonNull(intIpv4)) { 72 | logVO.setIpAddress(IPUtils.intToIpv4(intIpv4)); 73 | } 74 | return logVO; 75 | } 76 | 77 | @Override 78 | public LogEntity covertToEntity(LogVO logVO) { 79 | LogEntity logEntity = logVO.convertTo(new LogEntity()); 80 | boolean iPv4Valid = IPUtils.isIPv4Valid(logVO.getIpAddress()); 81 | if (iPv4Valid) { 82 | int intIpv4 = IPUtils.ipv4ToInt(logVO.getIpAddress()); 83 | logEntity.setIpAddress(intIpv4); 84 | } 85 | return logEntity; 86 | } 87 | 88 | 89 | @Override 90 | public List covertToListVO(@NonNull List logEntityList) { 91 | return logEntityList.stream().parallel() 92 | .map(this::covertToVO) 93 | .collect(Collectors.toList()); 94 | } 95 | 96 | @Override 97 | public PageResult covertToPageResult(IPage page) { 98 | List list = covertToListVO(page.getRecords()); 99 | return new PageResult<>(page.getTotal(), list); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/cache/CacheStore.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.cache; 2 | 3 | /** 4 | * 自定义缓存接口 5 | * 6 | * @author Myles Yang 7 | */ 8 | public interface CacheStore { 9 | 10 | /** 11 | * 设置一个会过期的缓存 12 | * 13 | * @param key 键 14 | * @param value 值 15 | * @param seconds 秒 16 | */ 17 | void set(K key, V value, int seconds); 18 | 19 | /** 20 | * 设置一个不会过期的缓存 21 | * 22 | * @param key 键 23 | * @param value 值 24 | */ 25 | void set(K key, V value); 26 | 27 | 28 | /** 29 | * 获取一个缓存 30 | * 31 | * @param key 键 32 | * @return 缓存值,如果键不存在返回 null 33 | */ 34 | V get(K key); 35 | 36 | /** 37 | * 删除一个缓存 38 | * 39 | * @param key 键 40 | * @return true : 删除成功 ; false : 键不存在 41 | */ 42 | boolean delete(K key); 43 | 44 | /** 45 | * 是否存在缓存 46 | * 47 | * @param key 键 48 | * @return true : 存在 ; false : 不存在 49 | */ 50 | boolean containsKey(K key); 51 | 52 | /** 53 | * 清空缓存 54 | */ 55 | void clear(); 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/cache/CacheWrapper.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.cache; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * 缓存数据包装类 9 | * 10 | * @param 数据类型 11 | */ 12 | @Data 13 | class CacheWrapper { 14 | 15 | /** 16 | * 缓存数据 17 | */ 18 | private V data; 19 | 20 | /** 21 | * 创建时间 22 | */ 23 | private Date createAt; 24 | 25 | /** 26 | * 过期时间 27 | */ 28 | private Date expireAt; 29 | 30 | public CacheWrapper(V data) { 31 | this.data = data; 32 | } 33 | 34 | public CacheWrapper(V data, Date createAt, Date expireAt) { 35 | this.data = data; 36 | this.createAt = createAt; 37 | this.expireAt = expireAt; 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/security/MyAuthenticationFailureHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.security; 2 | 3 | import org.springframework.security.authentication.BadCredentialsException; 4 | import org.springframework.security.core.AuthenticationException; 5 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 6 | import org.springframework.stereotype.Component; 7 | import xyz.snwjas.blog.annotation.ActionRecord; 8 | import xyz.snwjas.blog.constant.RS; 9 | import xyz.snwjas.blog.model.R; 10 | import xyz.snwjas.blog.model.enums.LogType; 11 | import xyz.snwjas.blog.utils.RUtils; 12 | import xyz.snwjas.blog.utils.RWriterUtils; 13 | 14 | import javax.servlet.ServletException; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | 19 | /** 20 | * 认证失败处理 21 | */ 22 | @Component 23 | public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { 24 | 25 | @ActionRecord(content = "#exception.getMessage()", type = LogType.LOGIN_FAILED) 26 | @Override 27 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response 28 | , AuthenticationException exception) throws IOException, ServletException { 29 | R result = RUtils.fail(exception instanceof BadCredentialsException 30 | ? RS.USERNAME_PASSWORD_ERROR 31 | : RS.SYSTEM_ERROR); 32 | RWriterUtils.writeJson(response, result); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/security/MyAuthenticationSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.security;/** 2 | * todo 3 | * 4 | * @author Myles Yang 5 | */ 6 | 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 11 | import org.springframework.stereotype.Component; 12 | import xyz.snwjas.blog.annotation.ActionRecord; 13 | import xyz.snwjas.blog.config.properties.MyBlogProperties; 14 | import xyz.snwjas.blog.constant.CacheKeyPrefix; 15 | import xyz.snwjas.blog.model.UserDetail; 16 | import xyz.snwjas.blog.model.enums.LogType; 17 | import xyz.snwjas.blog.model.vo.UserVO; 18 | import xyz.snwjas.blog.support.cache.MemoryCacheStore; 19 | import xyz.snwjas.blog.utils.IPUtils; 20 | import xyz.snwjas.blog.utils.RUtils; 21 | import xyz.snwjas.blog.utils.RWriterUtils; 22 | 23 | import javax.servlet.ServletException; 24 | import javax.servlet.http.HttpServletRequest; 25 | import javax.servlet.http.HttpServletResponse; 26 | import java.io.IOException; 27 | 28 | /** 29 | * 认证成功处理 30 | */ 31 | @Component 32 | @Slf4j 33 | public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { 34 | 35 | @Autowired 36 | private MemoryCacheStore memoryCacheStore; 37 | 38 | @Autowired 39 | private MyBlogProperties properties; 40 | 41 | @ActionRecord(content = "#authentication.getName()", type = LogType.LOGGED_IN) 42 | @Override 43 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response 44 | , Authentication authentication) throws IOException, ServletException { 45 | 46 | String ipAddress = IPUtils.getIpAddress(request); 47 | log.info("用户 [{}] 登录系统,IP:[{}]", authentication.getName(), ipAddress); 48 | // 登录成功,重置登录失败次数 49 | String key = CacheKeyPrefix.LOGIN_FAILED_COUNT + ipAddress; 50 | memoryCacheStore.set(key, 0, properties.getAllowLoginFailureSeconds()); 51 | // 返回登录结果 52 | UserDetail userDetails = (UserDetail) authentication.getPrincipal(); 53 | UserVO userVO = new UserVO().convertFrom(userDetails); 54 | RWriterUtils.writeJson(response, RUtils.success("登录成功", userVO)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/security/MyLogoutSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.security; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; 6 | import org.springframework.stereotype.Component; 7 | import xyz.snwjas.blog.annotation.ActionRecord; 8 | import xyz.snwjas.blog.model.R; 9 | import xyz.snwjas.blog.model.enums.LogType; 10 | import xyz.snwjas.blog.utils.IPUtils; 11 | import xyz.snwjas.blog.utils.RUtils; 12 | import xyz.snwjas.blog.utils.RWriterUtils; 13 | 14 | import javax.servlet.ServletException; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | 19 | /** 20 | * 注销成功处理 21 | */ 22 | @Component 23 | @Slf4j 24 | public class MyLogoutSuccessHandler implements LogoutSuccessHandler { 25 | 26 | @ActionRecord(value = "#authentication.getName()", type = LogType.LOGGED_OUT) 27 | @Override 28 | public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response 29 | , Authentication authentication) throws IOException, ServletException { 30 | log.info("用户 [{}] 注销登录,IP:[{}]", authentication.getName(), IPUtils.getIpAddress(request)); 31 | R result = RUtils.success("登录已注销"); 32 | RWriterUtils.writeJson(response, result); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/security/MyUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.security; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | import org.springframework.security.core.userdetails.UserDetailsService; 7 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 8 | import org.springframework.stereotype.Component; 9 | import xyz.snwjas.blog.config.properties.MyBlogProperties; 10 | import xyz.snwjas.blog.constant.CacheKeyPrefix; 11 | import xyz.snwjas.blog.constant.RS; 12 | import xyz.snwjas.blog.exception.ServiceException; 13 | import xyz.snwjas.blog.model.UserDetail; 14 | import xyz.snwjas.blog.service.impl.UserServiceImpl; 15 | import xyz.snwjas.blog.support.cache.MemoryCacheStore; 16 | import xyz.snwjas.blog.utils.IPUtils; 17 | 18 | import javax.servlet.http.HttpServletRequest; 19 | import java.util.Objects; 20 | import java.util.Optional; 21 | 22 | /** 23 | * 自定义登录业务逻辑 24 | */ 25 | @Component 26 | @Slf4j 27 | public class MyUserDetailsService implements UserDetailsService { 28 | 29 | @Autowired 30 | private MemoryCacheStore memoryCacheStore; 31 | 32 | @Autowired 33 | private MyBlogProperties properties; 34 | 35 | @Autowired 36 | private UserServiceImpl userService; 37 | 38 | @Override 39 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 40 | 41 | HttpServletRequest request = Optional.ofNullable(IPUtils.getRequest()) 42 | .orElseThrow(() -> new NullPointerException("获取IP地址出错")); 43 | 44 | String ipAddress = IPUtils.getIpAddress(request); 45 | 46 | String key = CacheKeyPrefix.LOGIN_FAILED_COUNT + ipAddress; 47 | // 获取登录失败的次数 48 | Integer count = (Integer) memoryCacheStore.get(key); 49 | // 如果同一IP在规定时间内连续认证错误xx次,则拒绝该ip登录 50 | if (Objects.nonNull(count)) { 51 | if (count < properties.getAllowLoginFailureCount()) { 52 | memoryCacheStore.set(key, count + 1, properties.getAllowLoginFailureSeconds()); 53 | } else { 54 | log.info("Ip: [{}] 多次登录失败", ipAddress); 55 | throw new ServiceException(RS.MULTIPLE_AUTHENTICATION_FAILURE); 56 | } 57 | } else { 58 | memoryCacheStore.set(key, 1, properties.getAllowLoginFailureSeconds()); 59 | } 60 | 61 | UserDetail userDetails = userService.getByUsername(username); 62 | if (Objects.isNull(userDetails)) { 63 | throw new UsernameNotFoundException("用户[" + username + "]不存在"); 64 | } 65 | 66 | return userDetails; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/wordfilter/EndType.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.wordfilter; 2 | 3 | /** 4 | * 结束类型定义 5 | * 6 | * @author minghu.zhang 7 | **/ 8 | public enum EndType { 9 | 10 | /** 11 | * 有下一个,结束 12 | */ 13 | HAS_NEXT, IS_END 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/wordfilter/FlagIndex.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.wordfilter; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 敏感词标记 7 | * 8 | * @author minghu.zhang 9 | */ 10 | public class FlagIndex { 11 | 12 | /** 13 | * 标记结果 14 | */ 15 | private boolean flag; 16 | /** 17 | * 是否白名单词汇 18 | */ 19 | private boolean isWhiteWord; 20 | /** 21 | * 标记索引 22 | */ 23 | private List index; 24 | 25 | public boolean isFlag() { 26 | return flag; 27 | } 28 | 29 | public void setFlag(boolean flag) { 30 | this.flag = flag; 31 | } 32 | 33 | public List getIndex() { 34 | return index; 35 | } 36 | 37 | public void setIndex(List index) { 38 | this.index = index; 39 | } 40 | 41 | public boolean isWhiteWord() { 42 | return isWhiteWord; 43 | } 44 | 45 | public void setWhiteWord(boolean whiteWord) { 46 | isWhiteWord = whiteWord; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/support/wordfilter/WordType.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.support.wordfilter; 2 | 3 | /** 4 | * 词汇类型 5 | * 6 | * @author minghu.zhang 7 | **/ 8 | public enum WordType { 9 | 10 | /** 11 | * 黑名单/白名单 12 | */ 13 | BLACK, WHITE 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/ClassUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.lang.annotation.Annotation; 6 | import java.net.URL; 7 | import java.util.ArrayList; 8 | import java.util.Enumeration; 9 | import java.util.List; 10 | import java.util.Objects; 11 | 12 | public class ClassUtils { 13 | 14 | /** 15 | * 根据一个接口返回该接口的所有类, 16 | * 注意实现类需要位于该接口的包下 17 | * 18 | * @param clazz 接口 19 | * @return List> 实现接口的所有类 20 | */ 21 | @SuppressWarnings("unchecked") 22 | public static List> getAllClassByInterface(Class clazz) { 23 | ArrayList> returnClassList = new ArrayList<>(); 24 | // 判断是不是接口,不是接口不作处理 25 | if (clazz.isInterface()) { 26 | // 获得当前包名 27 | String packageName = clazz.getPackage().getName(); 28 | try { 29 | // 获得当前包以及子包下的所有类 30 | List> allClass = getClasses(packageName); 31 | // 判断是否是一个接口 32 | for (Class aClass : allClass) { 33 | if (clazz.isAssignableFrom(aClass) && !clazz.equals(aClass)) { 34 | returnClassList.add((Class) aClass); 35 | } 36 | } 37 | } catch (Exception e) { 38 | // TODO: handle exception 39 | } 40 | } 41 | return returnClassList; 42 | } 43 | 44 | /** 45 | * 根据一个注解返回该接口的所有类, 46 | * 注意实现类需要位于该注解的包下 47 | * 48 | * @param clazz 注解 49 | * @return List> 实现接口的所有类 50 | */ 51 | public static List> getAllClassByAnnotation(Class clazz) { 52 | List> returnClassList = new ArrayList<>(); 53 | // 判断是不是注解 54 | if (clazz.isAnnotation()) { 55 | //获得当前包名 56 | String pkgName = clazz.getPackage().getName(); 57 | try { 58 | // 获得当前包以及子包下的所有类 59 | List> allClass = getClasses(pkgName); 60 | for (Class aClass : allClass) { 61 | if (aClass.isAnnotationPresent(clazz)) { 62 | returnClassList.add(aClass); 63 | } 64 | } 65 | } catch (Exception e) { 66 | // TODO: handle exception 67 | } 68 | } 69 | return returnClassList; 70 | } 71 | 72 | /** 73 | * 根据包名获得该包以及子包下的所有类不查找jar包中的 74 | * 75 | * @param pkgName 包名 76 | * @return List 包下所有类 77 | */ 78 | private static List> getClasses(String pkgName) throws ClassNotFoundException, IOException { 79 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 80 | String path = pkgName.replace(".", "/"); 81 | Enumeration resources = classLoader.getResources(path); 82 | List dirs = new ArrayList<>(); 83 | while (resources.hasMoreElements()) { 84 | URL resource = resources.nextElement(); 85 | String newPath = resource.getFile().replace("%20", " "); 86 | dirs.add(new File(newPath)); 87 | } 88 | ArrayList> classes = new ArrayList<>(); 89 | for (File directory : dirs) { 90 | classes.addAll(findClass(directory, pkgName)); 91 | } 92 | return classes; 93 | } 94 | 95 | private static List> findClass(File dir, String pkgName) throws ClassNotFoundException { 96 | List> classes = new ArrayList<>(); 97 | if (Objects.isNull(dir) || !dir.isDirectory()) { 98 | return classes; 99 | } 100 | File[] files = dir.listFiles(); 101 | for (File file : files) { 102 | if (file.isDirectory()) { 103 | assert !file.getName().contains("."); 104 | classes.addAll(findClass(file, pkgName + "." + file.getName())); 105 | } else if (file.getName().endsWith(".class")) { 106 | String className = pkgName + "." + file.getName().substring(0, file.getName().length() - 6); 107 | classes.add(Class.forName(className)); 108 | } 109 | } 110 | return classes; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/CodeAutoGenerator.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.annotation.FieldFill; 5 | import com.baomidou.mybatisplus.annotation.IdType; 6 | import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; 7 | import com.baomidou.mybatisplus.core.toolkit.StringUtils; 8 | import com.baomidou.mybatisplus.generator.AutoGenerator; 9 | import com.baomidou.mybatisplus.generator.config.DataSourceConfig; 10 | import com.baomidou.mybatisplus.generator.config.GlobalConfig; 11 | import com.baomidou.mybatisplus.generator.config.PackageConfig; 12 | import com.baomidou.mybatisplus.generator.config.StrategyConfig; 13 | import com.baomidou.mybatisplus.generator.config.po.TableFill; 14 | import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; 15 | 16 | import java.util.Arrays; 17 | import java.util.List; 18 | import java.util.Scanner; 19 | 20 | /** 21 | * MyBatis-Plus 的代码生成器 22 | * 23 | * @author Myles Yang 24 | */ 25 | public class CodeAutoGenerator { 26 | /** 27 | * 读取控制台内容 28 | */ 29 | public static String scanner(String tip) { 30 | Scanner scanner = new Scanner(System.in); 31 | System.out.println("请输入" + tip + ":"); 32 | if (scanner.hasNext()) { 33 | String ipt = scanner.next(); 34 | if (StringUtils.isNotBlank(ipt)) { 35 | return ipt; 36 | } 37 | } 38 | throw new MybatisPlusException("请输入正确的" + tip + "!"); 39 | } 40 | 41 | public static void main(String[] args) { 42 | String moduleName = scanner("模块名"); 43 | String[] tableNamesToGenerate = scanner("表名,多个英文逗号分割").split(","); 44 | 45 | // 代码生成器 46 | AutoGenerator mpg = new AutoGenerator(); 47 | 48 | // 项目根目录 49 | String projectPath = System.getProperty("user.dir"); 50 | 51 | //// 全局配置 52 | GlobalConfig gc = new GlobalConfig(); 53 | gc.setAuthor("snwjas") 54 | .setOutputDir(projectPath + "/src/main/java") 55 | .setSwagger2(true) 56 | // id生成策略 57 | .setIdType(IdType.AUTO) 58 | .setOpen(false); 59 | gc.setEntityName("%sEntity") 60 | .setControllerName("%sController") 61 | .setMapperName("%sMapper") 62 | .setXmlName("%sMapper") 63 | .setServiceName("%sService") 64 | .setServiceImplName("%sServiceImpl"); 65 | mpg.setGlobalConfig(gc); 66 | 67 | 68 | //// 数据源配置 69 | DataSourceConfig dsc = new DataSourceConfig(); 70 | dsc.setDbType(DbType.MYSQL) 71 | .setDriverName("com.mysql.cj.jdbc.Driver") 72 | .setUrl("jdbc:mysql://localhost:3306/myblogdb?useSSL=true&useUnicode=true" 73 | + "&characterEncoding=UTF8&serverTimezone=Asia/Shanghai") 74 | .setUsername("root") 75 | .setPassword("root"); 76 | mpg.setDataSource(dsc); 77 | 78 | 79 | //// 包配置 80 | PackageConfig pc = new PackageConfig(); 81 | pc.setModuleName(moduleName) 82 | .setParent("xyz.snwjas") 83 | .setEntity("model.entity") 84 | .setController("controller") 85 | .setMapper("mapper") 86 | .setXml("mapper.xml") 87 | .setService("service") 88 | .setServiceImpl("service.impl"); 89 | mpg.setPackageInfo(pc); 90 | 91 | 92 | // 自动填充字段 93 | List tableFillList = Arrays.asList( 94 | new TableFill("create_time", FieldFill.INSERT) 95 | , new TableFill("update_time", FieldFill.INSERT_UPDATE) 96 | ); 97 | 98 | //// 策略配置 99 | StrategyConfig strategy = new StrategyConfig(); 100 | strategy.setNaming(NamingStrategy.underline_to_camel) 101 | .setColumnNaming(NamingStrategy.underline_to_camel) 102 | .setEntityLombokModel(true) 103 | .setRestControllerStyle(true) 104 | // 字段自动填充 105 | .setTableFillList(tableFillList) 106 | // 逻辑删除字段名 107 | .setLogicDeleteFieldName("deleted") 108 | // 生成字段注解 109 | .setEntityTableFieldAnnotationEnable(true) 110 | // 需要生成的表的名字 111 | .setInclude(tableNamesToGenerate) 112 | // 驼峰转连字符:@RequestMapping("/addUser") -> @RequestMapping("/add-user") 113 | .setControllerMappingHyphenStyle(true); 114 | 115 | mpg.setStrategy(strategy); 116 | mpg.execute(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/DateUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import java.time.LocalDate; 4 | import java.time.LocalDateTime; 5 | import java.time.LocalTime; 6 | 7 | /** 8 | * Date Utils 9 | * 10 | * @author Myles Yang 11 | */ 12 | public class DateUtils { 13 | 14 | /** 15 | * 获取前 before 天的 零点时刻 16 | */ 17 | public static LocalDateTime getZeroDateTime(int before) { 18 | return LocalDateTime.of(LocalDate.now().minusDays(before), LocalTime.MIN); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import javax.imageio.ImageIO; 7 | import java.awt.image.BufferedImage; 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | /** 12 | * File Utils 13 | * 14 | * @author Myles Yang 15 | */ 16 | @Slf4j 17 | public class FileUtils { 18 | 19 | /** 20 | * 根据文件名获取文件名称与后缀 21 | * 22 | * @param fileName 23 | * @return [name, ext],后缀含"." 24 | */ 25 | public static String[] getFileNameAndExt(String fileName) { 26 | if (StringUtils.isEmpty(fileName)) { 27 | return new String[]{"", ""}; 28 | } 29 | int len = fileName.length(); 30 | int idx = fileName.lastIndexOf('.'); 31 | String name = fileName.substring(0, idx >= 0 ? idx : len); 32 | String ext = idx >= 0 && idx < len - 1 ? fileName.substring(idx, len) : ""; 33 | return new String[]{name, ext}; 34 | } 35 | 36 | /** 37 | * 文件名后添加字符串,如 word.txt 添加‘-java’, 效果 word-java.txt 38 | */ 39 | public static String fileNameAppend(String fileName, String append) { 40 | StringBuilder newFileName = new StringBuilder(fileName); 41 | 42 | // 是否有后缀 43 | int i = org.apache.commons.lang3.StringUtils.lastIndexOf(fileName, '.'); 44 | if (i >= 0) { 45 | newFileName.insert(i, append); 46 | } else { 47 | newFileName.append(append); 48 | } 49 | 50 | return newFileName.toString(); 51 | } 52 | 53 | /** 54 | * @param filePath 文件全路径 55 | * @return {-1: "文件不存在或是是文件夹", 0: "删除失败", 1: "删除成功"} 56 | */ 57 | public static int deleteFile(String filePath) { 58 | File file = new File(filePath); 59 | if (!file.exists() || file.isDirectory()) { 60 | return -1; 61 | } 62 | boolean deleted = file.delete(); 63 | return deleted ? 1 : 0; 64 | } 65 | 66 | /** 67 | * 获取图片像素 68 | * 69 | * @param imageFile 图片文件 70 | * @return 图片像素 {width, height} 71 | */ 72 | public static int[] getImagePixel(File imageFile) { 73 | try { 74 | BufferedImage bi = ImageIO.read(imageFile); 75 | int width = bi.getWidth(); 76 | int height = bi.getHeight(); 77 | return new int[]{width, height}; 78 | } catch (IOException e) { 79 | log.error("获取图片像素失败", e.getCause()); 80 | } 81 | return new int[]{0, 0}; 82 | } 83 | 84 | /** 85 | * 获取SpringWebMVC文件资源映射路径 86 | * 87 | * @param path 映射目录 88 | */ 89 | public static String getFileResLoc(String path) { 90 | if (StringUtils.isBlank(path)) { 91 | throw new IllegalArgumentException("参数不能为空"); 92 | } 93 | if (!path.endsWith(File.separator)) { 94 | path += File.separator; 95 | } 96 | return "file:" + path; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/IPUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.web.context.request.RequestContextHolder; 5 | import org.springframework.web.context.request.ServletRequestAttributes; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import java.util.Optional; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * IPv4 工具类 13 | */ 14 | public class IPUtils { 15 | 16 | /** 17 | * 检查 IPv4 是否合法 正则表达式 Pattern 对象 18 | */ 19 | private static final String _255 = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"; 20 | private static final Pattern PATTERN = Pattern.compile("^(?:" + _255 + "\\.){3}" + _255 + "$"); 21 | 22 | /** 23 | * 获取 HttpServletRequest 24 | */ 25 | public static HttpServletRequest getRequest() { 26 | return Optional.ofNullable(RequestContextHolder.getRequestAttributes()) 27 | .map(r -> ((ServletRequestAttributes) r).getRequest()) 28 | .orElse(null); 29 | } 30 | 31 | /** 32 | * IPv4地址是否有效 33 | */ 34 | public static boolean isIPv4Valid(String ipv4) { 35 | return ipv4 != null && PATTERN.matcher(ipv4).matches(); 36 | } 37 | 38 | /** 39 | * Int类型ip转为String类型 40 | */ 41 | public static String intToIpv4(int intIp) { 42 | int octet3 = (intIp >>> 24) % 256; 43 | int octet2 = (intIp >>> 16) % 256; 44 | int octet1 = (intIp >>> 8) % 256; 45 | // 负数时后8位处理 46 | int last = intIp % 256; 47 | int octet0 = last < 0 ? last + 256 : last; 48 | return octet3 + "." + octet2 + "." + octet1 + "." + octet0; 49 | } 50 | 51 | /** 52 | * String类型ip转为Int类型 53 | */ 54 | public static int ipv4ToInt(String ipv4) { 55 | if (isIPv4Valid(ipv4)) { 56 | String[] octets = ipv4.split("\\."); 57 | return (Integer.parseInt(octets[0]) << 24) 58 | + (Integer.parseInt(octets[1]) << 16) 59 | + (Integer.parseInt(octets[2]) << 8) 60 | + Integer.parseInt(octets[3]); 61 | } 62 | throw new IllegalArgumentException("IPv4字符串不合法!"); 63 | } 64 | 65 | /** 66 | * 是否为内网IP 67 | */ 68 | public static boolean isIPv4Private(String ipv4) { 69 | int intIp = ipv4ToInt(ipv4); 70 | return (intIp >= 167772160 && intIp <= 184549375) 71 | || (intIp >= -1408237568 && intIp <= -1407188993) 72 | || (intIp >= -1062731776 && intIp <= -1062666241); 73 | // return intIp >= ipv4ToInt("10.0.0.0") && intIp <= ipv4ToInt("10.255.255.255") 74 | // || intIp >= ipv4ToInt("172.16.0.0") && intIp <= ipv4ToInt("172.31.255.255") 75 | // || intIp >= ipv4ToInt("192.168.0.0") && intIp <= ipv4ToInt("192.168.255.255"); 76 | } 77 | 78 | /** 79 | * 获取请求主机IP地址 80 | * 如果通过代理进来,则透过防火墙获取真实IP地址 81 | */ 82 | public static String getIpAddress(HttpServletRequest request) { 83 | String ip = request.getHeader("X-Forwarded-For"); 84 | if (StringUtils.isNotEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) { 85 | // 多次反向代理后会有多个ip值,第一个ip才是真实ip 86 | int index = ip.indexOf(","); 87 | return index != -1 ? ip.substring(0, index) : ip; 88 | } 89 | ip = request.getHeader("X-Real-IP"); 90 | if (StringUtils.isNotEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) { 91 | return ip; 92 | } 93 | for (String hd : new String[]{"Proxy-Client-IP", "WL-Proxy-Client-IP", 94 | "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"}) { 95 | if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { 96 | ip = request.getHeader(hd); 97 | } 98 | } 99 | if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { 100 | ip = request.getRemoteAddr(); 101 | } 102 | return ip; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.DependsOn; 8 | import org.springframework.lang.NonNull; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.util.Assert; 11 | 12 | import java.io.IOException; 13 | import java.util.Map; 14 | 15 | /** 16 | * Json utilities. 17 | */ 18 | @Component 19 | @DependsOn("objectMapper") 20 | public class JsonUtils { 21 | 22 | private static ObjectMapper objectMapper; 23 | 24 | @Autowired 25 | public JsonUtils(ObjectMapper objectMapper) { 26 | JsonUtils.objectMapper = objectMapper; 27 | } 28 | 29 | 30 | public static T jsonToObject(@NonNull String json, @NonNull Class type) throws IOException { 31 | Assert.hasText(json, "Json content must not be blank"); 32 | Assert.notNull(type, "Target type must not be null"); 33 | 34 | return objectMapper.readValue(json, type); 35 | } 36 | 37 | 38 | public static String objectToJson(@NonNull Object source) throws JsonProcessingException { 39 | Assert.notNull(source, "Source object must not be null"); 40 | 41 | return objectMapper.writeValueAsString(source); 42 | } 43 | 44 | 45 | public static T mapToObject(@NonNull Map sourceMap, @NonNull Class type) throws IOException { 46 | Assert.notEmpty(sourceMap, "Source map must not be empty"); 47 | 48 | // Serialize the map 49 | String json = objectToJson(sourceMap); 50 | 51 | // Deserialize the json format of the map 52 | return jsonToObject(json, type); 53 | } 54 | 55 | 56 | public static Map objectToMap(@NonNull Object source) throws IOException { 57 | 58 | // Serialize the source object 59 | String json = objectToJson(source); 60 | 61 | // Deserialize the json 62 | return jsonToObject(json, Map.class); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/LambdaTypeUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.DependsOn; 8 | import org.springframework.stereotype.Component; 9 | import xyz.snwjas.blog.support.cache.MemoryCacheStore; 10 | 11 | import java.io.Serializable; 12 | import java.lang.invoke.SerializedLambda; 13 | import java.lang.reflect.Field; 14 | import java.lang.reflect.Method; 15 | 16 | /** 17 | * 通过 Lambda 获取对象属性名 18 | * 使用方法: LambdaTypeUtils.getFieldName(Test::getName) 19 | * 或 20 | * 使用MyBatis-plus 获取列名 21 | * 需要正确设置实体字段的的@TableField注解 22 | */ 23 | @Slf4j 24 | @Component 25 | @DependsOn("memoryCacheStore") 26 | public class LambdaTypeUtils { 27 | 28 | @FunctionalInterface 29 | public interface SFunction extends Serializable { 30 | Object get(T source); 31 | } 32 | 33 | private static final String SERIALIZED_LAMBDA_KEY = "SLK:"; 34 | 35 | private static final String COLUMN_NAME_KEY = "CNK:"; 36 | 37 | private static final int KEY_TIME_OUT_SECONDS = 86400; // one day 38 | 39 | private static MemoryCacheStore cache; 40 | 41 | @Autowired 42 | public LambdaTypeUtils(MemoryCacheStore cache) { 43 | LambdaTypeUtils.cache = cache; 44 | } 45 | 46 | /** 47 | * 获取字段名 48 | * 请确保正确设置实体字段的的@TableField注解 49 | */ 50 | public static String getColumnName(SFunction fn) { 51 | SerializedLambda lambda = getSerializedLambda(fn); 52 | if (null == lambda) return ""; 53 | 54 | String fullName = lambda.getImplClass().replaceAll("/", "."); 55 | String fieldName = getFieldName(fn); 56 | 57 | String columnName = (String) cache.get(COLUMN_NAME_KEY + fullName + "." + fieldName); 58 | if (null == columnName) { 59 | try { 60 | Class aClass = Class.forName(fullName); 61 | Field field = aClass.getDeclaredField(fieldName); 62 | TableField annotation = field.getAnnotation(TableField.class); 63 | columnName = annotation.value(); 64 | cache.set(COLUMN_NAME_KEY + fullName + "." + fieldName, columnName, KEY_TIME_OUT_SECONDS); 65 | } catch (NoSuchFieldException | ClassNotFoundException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | return columnName; 70 | } 71 | 72 | /*** 73 | * 转换方法引用为属性名 74 | */ 75 | public static String getFieldName(SFunction fn) { 76 | SerializedLambda lambda = getSerializedLambda(fn); 77 | if (null == lambda) return ""; 78 | // 获取方法名 79 | String methodName = lambda.getImplMethodName(); 80 | String prefix = null; 81 | if (methodName.startsWith("get")) { 82 | prefix = "get"; 83 | } else if (methodName.startsWith("is")) { 84 | prefix = "is"; 85 | } 86 | if (null == prefix) { 87 | log.error("无效的getter方法:{}", methodName); 88 | return ""; 89 | } 90 | // 截取get/is之后的字符串并转换首字母为小写 91 | return toLowerCaseFirstOne(methodName.replace(prefix, "")); 92 | } 93 | 94 | /** 95 | * 首字母转小写 96 | */ 97 | private static String toLowerCaseFirstOne(String s) { 98 | if (Character.isLowerCase(s.charAt(0))) { 99 | return s; 100 | } else { 101 | return Character.toLowerCase(s.charAt(0)) + s.substring(1); 102 | } 103 | } 104 | 105 | /** 106 | * 关键在于这个方法 107 | */ 108 | private static SerializedLambda getSerializedLambda(Serializable fn) { 109 | SerializedLambda lambda = (SerializedLambda) cache.get(SERIALIZED_LAMBDA_KEY + fn); 110 | if (null == lambda) { 111 | try { 112 | // 提取SerializedLambda并缓存 113 | Method method = fn.getClass().getDeclaredMethod("writeReplace"); 114 | method.setAccessible(Boolean.TRUE); 115 | lambda = (SerializedLambda) method.invoke(fn); 116 | cache.set(SERIALIZED_LAMBDA_KEY + fn, lambda, KEY_TIME_OUT_SECONDS); 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | return lambda; 122 | } 123 | 124 | 125 | } 126 | 127 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/RUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import xyz.snwjas.blog.constant.RS; 5 | import xyz.snwjas.blog.model.R; 6 | 7 | /** 8 | * 统一 响应 工具 9 | * 10 | * @author Myles Yang 11 | */ 12 | public class RUtils { 13 | 14 | private static final Object EMPTY_DATA = null; 15 | 16 | /** 17 | * 返回 统一 响应体 18 | * 19 | * @param status 状态码 20 | * @param message 消息 21 | * @param obj 数据 22 | * @return R 23 | */ 24 | public static R result(int status, String message, Object obj) { 25 | return new R(status, message, obj); 26 | } 27 | 28 | /** 29 | * 普通的成功失败响应 30 | * 31 | * @param i 小于1失败,否则成功 32 | * @param message 消息 33 | */ 34 | public static R commonFailOrNot(int i, String message) { 35 | if (i < 1) { 36 | return fail(message + "失败"); 37 | } 38 | return success(message + "成功"); 39 | } 40 | 41 | 42 | public static R success(String message, Object data) { 43 | return result(RS.SUCCESS.status(), message, data); 44 | } 45 | 46 | public static R success(String message) { 47 | return success(message, EMPTY_DATA); 48 | } 49 | 50 | public static R succeed() { 51 | return success(RS.SUCCESS.message()); 52 | } 53 | 54 | /////////////////////////////////////////////////////////////////////////////// 55 | 56 | /** 57 | * 失败,不传 status ,默认系统错误 58 | */ 59 | public static R fail(String message, Object data) { 60 | return result(RS.SYSTEM_ERROR.status(), message, data); 61 | } 62 | 63 | public static R fail(String message) { 64 | return fail(message, EMPTY_DATA); 65 | } 66 | 67 | 68 | public static R fail(RS status, Object data) { 69 | return result(status.status(), status.message(), data); 70 | } 71 | 72 | public static R fail(RS status) { 73 | return result(status.status(), status.message(), EMPTY_DATA); 74 | } 75 | 76 | 77 | public static R fail(HttpStatus httpStatus, Object data) { 78 | return result(httpStatus.value(), httpStatus.getReasonPhrase(), data); 79 | } 80 | 81 | public static R fail(HttpStatus httpStatus) { 82 | return fail(httpStatus, EMPTY_DATA); 83 | } 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/RWriterUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import xyz.snwjas.blog.model.R; 5 | 6 | import javax.servlet.ServletResponse; 7 | import java.io.IOException; 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * 返回 响应 结果 工具 12 | * 13 | * @author Myles Yang 14 | */ 15 | public class RWriterUtils { 16 | 17 | /** 18 | * 默认编码 19 | */ 20 | private static final String DEFAULT_ENCODING = StandardCharsets.UTF_8.name(); 21 | 22 | 23 | public static void writeTextHtml(ServletResponse response, String textHtml) { 24 | writeString(response, "text/html", DEFAULT_ENCODING, textHtml); 25 | } 26 | 27 | public static void writeJson(ServletResponse response, String jsonString) { 28 | writeString(response, "application/json", DEFAULT_ENCODING, jsonString); 29 | } 30 | 31 | public static void writeJson(ServletResponse response, R r) { 32 | try { 33 | writeJson(response, JsonUtils.objectToJson(r)); 34 | } catch (JsonProcessingException e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | public static void writeString(ServletResponse response, String contentType, String encoding, String string) { 40 | try { 41 | response.setContentType(contentType + ";charset=" + encoding); 42 | response.getWriter().write(string); 43 | response.getWriter().close(); 44 | } catch (IOException e) { 45 | throw new RuntimeException(e); 46 | } 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/StrUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * String Utils 7 | * 8 | * @author Myles Yang 9 | */ 10 | public class StrUtils { 11 | 12 | /** 13 | * 移除字符串中的空白字符 14 | */ 15 | public static String removeBlank(String text) { 16 | if (StringUtils.isBlank(text)) { 17 | return ""; 18 | } 19 | StringBuilder newString = new StringBuilder(); 20 | char[] chars = text.toCharArray(); 21 | for (char c : chars) { 22 | if (Character.isWhitespace(c)) { 23 | continue; 24 | } 25 | newString.append(c); 26 | } 27 | return newString.toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/xyz/snwjas/blog/utils/URLUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.snwjas.blog.utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLDecoder; 5 | import java.net.URLEncoder; 6 | import java.nio.charset.StandardCharsets; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * URL utils 12 | * 13 | * @author Myles Yang 14 | */ 15 | public class URLUtils { 16 | 17 | public static final String DEFAULT_ENCODING = StandardCharsets.UTF_8.name(); 18 | 19 | // URL 中合法字符 20 | public static final Pattern PATTERN = Pattern.compile("[^0-9a-zA-Z-_.~!*'();:@&=+$,/?#\\[\\]]"); 21 | 22 | /** 23 | * 编码整个链接 24 | */ 25 | public static String encodeAll(String url) { 26 | Matcher m = PATTERN.matcher(url); 27 | StringBuffer b = new StringBuffer(); 28 | try { 29 | while (m.find()) { 30 | m.appendReplacement(b, URLEncoder.encode(m.group(0), DEFAULT_ENCODING)); 31 | } 32 | } catch (UnsupportedEncodingException e) { 33 | e.printStackTrace(); 34 | return ""; 35 | } 36 | m.appendTail(b); 37 | return b.toString(); 38 | } 39 | 40 | /** 41 | * 编码链接的最后部分 42 | * 如:localhost:9527//index/img-百度logo.jpg,只编码 img-百度logo.jpg 43 | */ 44 | public static String encodeLast(String url) { 45 | int i = url.lastIndexOf('/'); 46 | String last = url.substring(i + 1); 47 | String encodedLast; 48 | try { 49 | encodedLast = URLEncoder.encode(last, DEFAULT_ENCODING); 50 | } catch (UnsupportedEncodingException e) { 51 | e.printStackTrace(); 52 | return ""; 53 | } 54 | return url.substring(0, i) + encodedLast; 55 | } 56 | 57 | public static String decode(String str) { 58 | try { 59 | return URLDecoder.decode(str, DEFAULT_ENCODING); 60 | } catch (UnsupportedEncodingException e) { 61 | throw new RuntimeException(e); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/resources/admin/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep 4 | -------------------------------------------------------------------------------- /src/main/resources/app/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep 4 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9096 3 | tomcat: 4 | max-swallow-size: -1 5 | 6 | spring: 7 | profiles: dev 8 | datasource: 9 | driver-class-name: com.mysql.jdbc.Driver 10 | url: jdbc:mysql://localhost:3306/myblogdb?allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai 11 | username: root 12 | password: root 13 | servlet: 14 | multipart: 15 | max-file-size: 10MB 16 | max-request-size: 100MB 17 | 18 | mybatis-plus: 19 | configuration: 20 | # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 21 | default-enum-type-handler: com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler 22 | mapper-locations: 23 | - classpath:xyz/snwjas/blog/mapper/xml/*.xml 24 | 25 | my-blog: 26 | doc-enable: true 27 | admin-path: admin 28 | 29 | -------------------------------------------------------------------------------- /src/main/resources/application-prod.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9096 3 | tomcat: 4 | max-swallow-size: -1 5 | 6 | spring: 7 | profiles: prod 8 | datasource: 9 | driver-class-name: com.mysql.jdbc.Driver 10 | url: jdbc:mysql://localhost:3306/myblogdb?allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai 11 | username: root 12 | password: root 13 | servlet: 14 | multipart: 15 | max-file-size: 10MB 16 | max-request-size: 100MB 17 | 18 | mybatis-plus: 19 | configuration: 20 | # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 21 | default-enum-type-handler: com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler 22 | mapper-locations: 23 | - classpath:xyz/snwjas/blog/mapper/xml/*.xml 24 | 25 | my-blog: 26 | doc-enable: false 27 | admin-path: madmin 28 | 29 | -------------------------------------------------------------------------------- /src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: dev 4 | # active: prod 5 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BLUE} 2 | __ __ ____ _ 3 | | \/ | _ _ | __ ) | | ___ __ _ 4 | | |\/| | | | | | | _ \ | | / _ \ / _` | 5 | | | | | | |_| | | |_) | | | | (_) | | (_| | 6 | |_| |_| \__, | |____/ |_| \___/ \__, | 7 | |___/ |___/ 8 | ${AnsiColor.BLUE} 9 | Version: ${application.version} 10 | -------------------------------------------------------------------------------- /src/main/resources/static/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep 4 | -------------------------------------------------------------------------------- /src/test/java/xyz/snwjas/blog/.gitkeep: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file !.gitkeep 4 | --------------------------------------------------------------------------------