├── .gitignore ├── README.md ├── blog ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── pjb │ │ │ └── blog │ │ │ ├── BlogApplication.java │ │ │ ├── config │ │ │ └── SecurityConfig.java │ │ │ ├── controller │ │ │ ├── AdminController.java │ │ │ ├── BlogController.java │ │ │ ├── CatalogController.java │ │ │ ├── CommentController.java │ │ │ ├── HelloController.java │ │ │ ├── MainController.java │ │ │ ├── UserController.java │ │ │ ├── UserspaceController.java │ │ │ └── VoteController.java │ │ │ ├── domain │ │ │ ├── Authority.java │ │ │ ├── Blog.java │ │ │ ├── Catalog.java │ │ │ ├── Comment.java │ │ │ ├── User.java │ │ │ ├── Vote.java │ │ │ └── es │ │ │ │ └── EsBlog.java │ │ │ ├── repository │ │ │ ├── AuthorityRepository.java │ │ │ ├── BlogRepository.java │ │ │ ├── CatalogRepository.java │ │ │ ├── CommentRepository.java │ │ │ ├── UserRepository.java │ │ │ ├── VoteRepository.java │ │ │ └── es │ │ │ │ └── EsBlogRepository.java │ │ │ ├── service │ │ │ ├── AuthorityService.java │ │ │ ├── AuthorityServiceImpl.java │ │ │ ├── BlogService.java │ │ │ ├── BlogServiceImpl.java │ │ │ ├── CatalogService.java │ │ │ ├── CatalogServiceImpl.java │ │ │ ├── CommentService.java │ │ │ ├── CommentServiceImpl.java │ │ │ ├── EsBlogService.java │ │ │ ├── EsBlogServiceImpl.java │ │ │ ├── UserService.java │ │ │ ├── UserServiceImpl.java │ │ │ ├── VoteService.java │ │ │ └── VoteServiceImpl.java │ │ │ ├── util │ │ │ └── ConstraintViolationExceptionHandler.java │ │ │ └── vo │ │ │ ├── CatalogVO.java │ │ │ ├── Menu.java │ │ │ ├── Response.java │ │ │ └── TagVO.java │ └── resources │ │ ├── application.yml │ │ ├── static │ │ ├── css │ │ │ ├── blog.css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap-table.css │ │ │ ├── bootstrap-table.min.css │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ ├── bootstrap.min.css.map │ │ │ ├── component-chosen.css │ │ │ ├── component-chosen.min.css │ │ │ ├── cropbox.css │ │ │ ├── emoji │ │ │ │ ├── Sysmbols.css │ │ │ │ ├── nature.css │ │ │ │ ├── object.css │ │ │ │ ├── people.css │ │ │ │ ├── place.css │ │ │ │ └── twemoji.css │ │ │ ├── font-awesome.css │ │ │ ├── font-awesome.css.map │ │ │ ├── font-awesome.min.css │ │ │ ├── images │ │ │ │ └── emoji │ │ │ │ │ ├── Sysmbols.png │ │ │ │ │ ├── nature.png │ │ │ │ │ ├── object.png │ │ │ │ │ ├── people.png │ │ │ │ │ ├── place.png │ │ │ │ │ └── twemoji.png │ │ │ ├── jquery.tagsinput.min.css │ │ │ ├── nprogress.css │ │ │ ├── style.css │ │ │ ├── tether-theme-arrows-dark.css │ │ │ ├── tether-theme-arrows-dark.min.css │ │ │ ├── tether-theme-arrows.css │ │ │ ├── tether-theme-arrows.min.css │ │ │ ├── tether-theme-basic.css │ │ │ ├── tether-theme-basic.min.css │ │ │ ├── tether.css │ │ │ ├── tether.min.css │ │ │ ├── thinker-md.vendor.css │ │ │ ├── thymeleaf-bootstrap-paginator.css │ │ │ ├── toastr.css │ │ │ └── toastr.min.css │ │ ├── data │ │ │ └── data1.json │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── gly-halflings-regular.eot │ │ │ ├── gly-halflings-regular.svg │ │ │ ├── gly-halflings-regular.ttf │ │ │ ├── gly-halflings-regular.woff │ │ │ └── gly-halflings-regular.woff2 │ │ ├── images │ │ │ ├── avatar-defualt.jpg │ │ │ └── delete.png │ │ └── js │ │ │ ├── admins │ │ │ └── main.js │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.min.js │ │ │ ├── catalog-generator.js │ │ │ ├── chosen.jquery.js │ │ │ ├── cropbox.js │ │ │ ├── index.js │ │ │ ├── jquery-3.1.1.min.js │ │ │ ├── jquery.form.min.js │ │ │ ├── jquery.tagsinput.min.js │ │ │ ├── main.js │ │ │ ├── nprogress.js │ │ │ ├── tether.js │ │ │ ├── tether.min.js │ │ │ ├── thinker-md.vendor.js │ │ │ ├── thinker-md.vendor.min.js │ │ │ ├── thinker-md.vendor.min.map │ │ │ ├── thymeleaf-bootstrap-paginator.js │ │ │ ├── toastr.min.js │ │ │ ├── users │ │ │ └── main.js │ │ │ └── userspace │ │ │ ├── blog.js │ │ │ ├── blogedit.js │ │ │ ├── main.js │ │ │ └── u.js │ │ └── templates │ │ ├── admins │ │ └── index.html │ │ ├── fragments │ │ ├── footer.html │ │ ├── header.html │ │ └── page.html │ │ ├── index.html │ │ ├── login.html │ │ ├── register.html │ │ ├── search.html │ │ ├── users │ │ ├── add.html │ │ ├── edit.html │ │ └── list.html │ │ └── userspace │ │ ├── avatar.html │ │ ├── blog.html │ │ ├── blogedit.html │ │ ├── catalogedit.html │ │ ├── profile.html │ │ └── u.html │ └── test │ └── java │ └── com │ └── pjb │ └── blog │ └── BlogApplicationTests.java ├── fileserver ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── pjb │ │ │ └── fileserver │ │ │ ├── FileserverApplication.java │ │ │ ├── controller │ │ │ └── FileController.java │ │ │ ├── domain │ │ │ └── File.java │ │ │ ├── repository │ │ │ └── FileRepository.java │ │ │ ├── service │ │ │ ├── FileService.java │ │ │ └── FileServiceImpl.java │ │ │ └── util │ │ │ └── MD5Util.java │ └── resources │ │ ├── application.yml │ │ ├── static │ │ └── timg.jpg │ │ └── templates │ │ ├── fragments │ │ ├── footer.html │ │ └── header.html │ │ └── index.html │ └── test │ └── java │ └── com │ └── pjb │ └── fileserver │ └── FileserverApplicationTests.java └── import.sql /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/README.md -------------------------------------------------------------------------------- /blog/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /blog/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.1.RELEASE 9 | 10 | 11 | com.pjb 12 | blog 13 | 0.0.1-SNAPSHOT 14 | blog 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-data-elasticsearch 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-security 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-thymeleaf 37 | 38 | 39 | org.thymeleaf.extras 40 | thymeleaf-extras-springsecurity5 41 | 3.0.4.RELEASE 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-web 46 | 47 | 48 | 49 | mysql 50 | mysql-connector-java 51 | runtime 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-test 56 | test 57 | 58 | 59 | org.apache.commons 60 | commons-lang3 61 | 3.8.1 62 | 63 | 64 | org.springframework.security 65 | spring-security-test 66 | test 67 | 68 | 69 | es.nitaur.markdown 70 | txtmark 71 | 0.16 72 | 73 | 74 | org.projectlombok 75 | lombok 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-maven-plugin 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/BlogApplication.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; 6 | 7 | @SpringBootApplication 8 | public class BlogApplication { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(BlogApplication.class, args); 12 | } 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.security.authentication.AuthenticationProvider; 6 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 12 | import org.springframework.security.core.userdetails.UserDetailsService; 13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 | import org.springframework.security.crypto.password.PasswordEncoder; 15 | 16 | /** 17 | * Spring Security 配置类. 18 | */ 19 | @EnableWebSecurity 20 | @EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法安全设置 21 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 22 | 23 | private static final String KEY = "ben"; 24 | 25 | @Autowired 26 | private UserDetailsService userDetailsService; 27 | 28 | @Autowired 29 | private PasswordEncoder passwordEncoder; 30 | 31 | @Bean 32 | public PasswordEncoder passwordEncoder() { 33 | return new BCryptPasswordEncoder(); // 使用 BCrypt 加密 34 | } 35 | 36 | @Bean 37 | public AuthenticationProvider authenticationProvider() { 38 | DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); 39 | authenticationProvider.setUserDetailsService(userDetailsService); 40 | authenticationProvider.setPasswordEncoder(passwordEncoder); // 设置密码加密方式 41 | return authenticationProvider; 42 | } 43 | 44 | /** 45 | * 自定义配置 46 | */ 47 | @Override 48 | protected void configure(HttpSecurity http) throws Exception { 49 | http.authorizeRequests().antMatchers("/css/**", "/js/**", "/fonts/**", "/index").permitAll() // 都可以访问 50 | .antMatchers("/h2-console/**").permitAll() // 都可以访问 51 | .antMatchers("/admins/**").hasRole("ADMIN") // 需要相应的角色才能访问 52 | .and() 53 | .formLogin() //基于 Form 表单登录验证 54 | .loginPage("/login").failureUrl("/login-error") // 自定义登录界面 55 | .and().rememberMe().key(KEY) // 启用 remember me 56 | .and().exceptionHandling().accessDeniedPage("/403"); // 处理异常,拒绝访问就重定向到 403 页面 57 | http.csrf().ignoringAntMatchers("/h2-console/**"); // 禁用 H2 控制台的 CSRF 防护 58 | http.headers().frameOptions().sameOrigin(); // 允许来自同一来源的H2 控制台的请求 59 | } 60 | 61 | /** 62 | * 认证信息管理 63 | */ 64 | @Autowired 65 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 66 | auth.userDetailsService(userDetailsService); 67 | auth.authenticationProvider(authenticationProvider()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/AdminController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import com.pjb.blog.vo.Menu; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.ui.Model; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.servlet.ModelAndView; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | 14 | /** 15 | * 用户控制器. 16 | */ 17 | @Controller 18 | @RequestMapping("/admins") 19 | public class AdminController { 20 | 21 | /** 22 | * 获取后台管理主页面 23 | */ 24 | @GetMapping 25 | public ModelAndView listUsers(Model model) { 26 | List list = new ArrayList<>(); 27 | list.add(new Menu("用户管理", "/users")); 28 | model.addAttribute("list", list); 29 | return new ModelAndView("/admins/index", "model", model); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/BlogController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import com.pjb.blog.domain.User; 4 | import com.pjb.blog.domain.es.EsBlog; 5 | import com.pjb.blog.service.EsBlogService; 6 | import com.pjb.blog.vo.TagVO; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.data.domain.Page; 9 | import org.springframework.data.domain.PageRequest; 10 | import org.springframework.data.domain.Pageable; 11 | import org.springframework.data.domain.Sort; 12 | import org.springframework.data.domain.Sort.Direction; 13 | import org.springframework.stereotype.Controller; 14 | import org.springframework.ui.Model; 15 | import org.springframework.web.bind.annotation.GetMapping; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestParam; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * 主页控制器. 23 | */ 24 | @Controller 25 | @RequestMapping("/blogs") 26 | public class BlogController { 27 | 28 | private final EsBlogService esBlogService; 29 | 30 | @Autowired 31 | public BlogController(EsBlogService esBlogService) { 32 | this.esBlogService = esBlogService; 33 | } 34 | 35 | @GetMapping 36 | public String listEsBlogs( 37 | @RequestParam(value = "order", required = false, defaultValue = "new") String order, 38 | @RequestParam(value = "keyword", required = false, defaultValue = "") String keyword, 39 | @RequestParam(value = "async", required = false) boolean async, 40 | @RequestParam(value = "pageIndex", required = false, defaultValue = "0") int pageIndex, 41 | @RequestParam(value = "pageSize", required = false, defaultValue = "10") int pageSize, 42 | Model model) { 43 | 44 | Page page = null; 45 | List list = null; 46 | boolean isEmpty = true; // 系统初始化时,没有博客数据 47 | try { 48 | if (order.equals("hot")) { // 最热查询 49 | Sort sort = new Sort(Direction.DESC, "readSize", "commentSize", "voteSize", "createTime"); 50 | Pageable pageable = PageRequest.of(pageIndex, pageSize, sort); 51 | page = esBlogService.listHotestEsBlogs(keyword, pageable); 52 | } else if (order.equals("new")) { // 最新查询 53 | Sort sort = new Sort(Direction.DESC, "createTime"); 54 | Pageable pageable = PageRequest.of(pageIndex, pageSize, sort); 55 | page = esBlogService.listNewestEsBlogs(keyword, pageable); 56 | } 57 | 58 | isEmpty = false; 59 | } catch (Exception e) { 60 | Pageable pageable = PageRequest.of(pageIndex, pageSize); 61 | page = esBlogService.listEsBlogs(pageable); 62 | } 63 | 64 | if (page != null) { 65 | list = page.getContent(); // 当前所在页面数据列表 66 | } 67 | 68 | 69 | model.addAttribute("order", order); 70 | model.addAttribute("keyword", keyword); 71 | model.addAttribute("page", page); 72 | model.addAttribute("blogList", list); 73 | 74 | // 首次访问页面才加载 75 | if (!async && !isEmpty) { 76 | List newest = esBlogService.listTop5NewestEsBlogs(); 77 | model.addAttribute("newest", newest); 78 | List hotest = esBlogService.listTop5HotestEsBlogs(); 79 | model.addAttribute("hotest", hotest); 80 | List tags = esBlogService.listTop30Tags(); 81 | model.addAttribute("tags", tags); 82 | List users = esBlogService.listTop12Users(); 83 | model.addAttribute("users", users); 84 | } 85 | 86 | return (async ? "/index :: #mainContainerRepleace" : "/index"); 87 | } 88 | 89 | @GetMapping("/newest") 90 | public String listNewestEsBlogs(Model model) { 91 | List newest = esBlogService.listTop5NewestEsBlogs(); 92 | model.addAttribute("newest", newest); 93 | return "newest"; 94 | } 95 | 96 | @GetMapping("/hotest") 97 | public String listHotestEsBlogs(Model model) { 98 | List hotest = esBlogService.listTop5HotestEsBlogs(); 99 | model.addAttribute("hotest", hotest); 100 | return "hotest"; 101 | } 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/CatalogController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import com.pjb.blog.domain.Catalog; 4 | import com.pjb.blog.domain.User; 5 | import com.pjb.blog.service.CatalogService; 6 | import com.pjb.blog.util.ConstraintViolationExceptionHandler; 7 | import com.pjb.blog.vo.CatalogVO; 8 | import com.pjb.blog.vo.Response; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.security.core.context.SecurityContextHolder; 13 | import org.springframework.security.core.userdetails.UserDetailsService; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import javax.validation.ConstraintViolationException; 19 | import java.util.List; 20 | 21 | 22 | /** 23 | * 分类 控制器. 24 | */ 25 | @Controller 26 | @RequestMapping("/catalogs") 27 | public class CatalogController { 28 | 29 | private final CatalogService catalogService; 30 | private final UserDetailsService userDetailsService; 31 | 32 | @Autowired 33 | public CatalogController(CatalogService catalogService,UserDetailsService userDetailsService) { 34 | this.catalogService = catalogService; 35 | this.userDetailsService = userDetailsService; 36 | } 37 | 38 | /** 39 | * 获取分类列表 40 | */ 41 | @GetMapping 42 | public String listComments(@RequestParam(value="username") String username, Model model) { 43 | User user = (User)userDetailsService.loadUserByUsername(username); 44 | List catalogs = catalogService.listCatalogs(user); 45 | 46 | // 判断操作用户是否是分类的所有者 47 | boolean isOwner = false; 48 | 49 | if (SecurityContextHolder.getContext().getAuthentication() !=null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated() 50 | && !"anonymousUser".equals(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString())) { 51 | User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 52 | if (principal !=null && user.getUsername().equals(principal.getUsername())) { 53 | isOwner = true; 54 | } 55 | } 56 | 57 | model.addAttribute("isCatalogsOwner", isOwner); 58 | model.addAttribute("catalogs", catalogs); 59 | return "/userspace/u :: #catalogRepleace"; 60 | } 61 | /** 62 | * 发表分类 63 | */ 64 | @PostMapping 65 | @PreAuthorize("authentication.name.equals(#catalogVO.username)")// 指定用户才能操作方法 66 | public ResponseEntity create(@RequestBody CatalogVO catalogVO) { 67 | 68 | String username = catalogVO.getUsername(); 69 | Catalog catalog = catalogVO.getCatalog(); 70 | 71 | User user = (User)userDetailsService.loadUserByUsername(username); 72 | 73 | try { 74 | catalog.setUser(user); 75 | catalogService.saveCatalog(catalog); 76 | } catch (ConstraintViolationException e) { 77 | return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e))); 78 | } catch (Exception e) { 79 | return ResponseEntity.ok().body(new Response(false, e.getMessage())); 80 | } 81 | 82 | return ResponseEntity.ok().body(new Response(true, "处理成功", null)); 83 | } 84 | 85 | /** 86 | * 删除分类 87 | */ 88 | @DeleteMapping("/{id}") 89 | @PreAuthorize("authentication.name.equals(#username)") // 指定用户才能操作方法 90 | public ResponseEntity delete(String username, @PathVariable("id") Long id) { 91 | try { 92 | catalogService.removeCatalog(id); 93 | } catch (ConstraintViolationException e) { 94 | return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e))); 95 | } catch (Exception e) { 96 | return ResponseEntity.ok().body(new Response(false, e.getMessage())); 97 | } 98 | 99 | return ResponseEntity.ok().body(new Response(true, "处理成功", null)); 100 | } 101 | 102 | /** 103 | * 获取分类编辑界面 104 | */ 105 | @GetMapping("/edit") 106 | public String getCatalogEdit(Model model) { 107 | Catalog catalog = new Catalog(null, null); 108 | model.addAttribute("catalog",catalog); 109 | return "/userspace/catalogedit"; 110 | } 111 | /** 112 | * 根据 Id 获取分类信息 113 | */ 114 | @GetMapping("/edit/{id}") 115 | public String getCatalogById(@PathVariable("id") Long id, Model model) { 116 | Catalog catalog = catalogService.getCatalogById(id); 117 | model.addAttribute("catalog",catalog); 118 | return "/userspace/catalogedit"; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/CommentController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import com.pjb.blog.domain.Blog; 4 | import com.pjb.blog.domain.Comment; 5 | import com.pjb.blog.domain.User; 6 | import com.pjb.blog.service.BlogService; 7 | import com.pjb.blog.service.CommentService; 8 | import com.pjb.blog.util.ConstraintViolationExceptionHandler; 9 | import com.pjb.blog.vo.Response; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.security.access.prepost.PreAuthorize; 13 | import org.springframework.security.core.context.SecurityContextHolder; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import javax.validation.ConstraintViolationException; 19 | import java.util.List; 20 | 21 | 22 | /** 23 | * 评论 控制器. 24 | */ 25 | @Controller 26 | @RequestMapping("/comments") 27 | public class CommentController { 28 | 29 | private final BlogService blogService; 30 | 31 | private final CommentService commentService; 32 | 33 | @Autowired 34 | public CommentController(BlogService blogService, CommentService commentService) { 35 | this.blogService = blogService; 36 | this.commentService = commentService; 37 | } 38 | 39 | /** 40 | * 获取评论列表 41 | */ 42 | @GetMapping 43 | public String listComments(@RequestParam(value="blogId") Long blogId, Model model) { 44 | Blog blog = blogService.getBlogById(blogId); 45 | List comments = blog.getComments(); 46 | 47 | // 判断操作用户是否是评论的所有者 48 | String commentOwner = ""; 49 | if (SecurityContextHolder.getContext().getAuthentication() !=null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated() 50 | && !"anonymousUser".equals(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString())) { 51 | User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 52 | if (principal !=null) { 53 | commentOwner = principal.getUsername(); 54 | } 55 | } 56 | 57 | model.addAttribute("commentOwner", commentOwner); 58 | model.addAttribute("comments", comments); 59 | return "/userspace/blog :: #mainContainerRepleace"; 60 | } 61 | /** 62 | * 发表评论 63 | */ 64 | @PostMapping 65 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN','ROLE_USER')") // 指定角色权限才能操作方法 66 | public ResponseEntity createComment(Long blogId, String commentContent) { 67 | 68 | try { 69 | blogService.createComment(blogId, commentContent); 70 | } catch (ConstraintViolationException e) { 71 | return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e))); 72 | } catch (Exception e) { 73 | return ResponseEntity.ok().body(new Response(false, e.getMessage())); 74 | } 75 | 76 | return ResponseEntity.ok().body(new Response(true, "处理成功", null)); 77 | } 78 | 79 | /** 80 | * 删除评论 81 | */ 82 | @DeleteMapping("/{id}") 83 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN','ROLE_USER')") // 指定角色权限才能操作方法 84 | public ResponseEntity delete(@PathVariable("id") Long id, Long blogId) { 85 | 86 | boolean isOwner = false; 87 | User user = commentService.getCommentById(id).getUser(); 88 | 89 | // 判断操作用户是否是评论的所有者 90 | if (SecurityContextHolder.getContext().getAuthentication() !=null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated() 91 | && !"anonymousUser".equals(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString())) { 92 | User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 93 | if (principal !=null && user.getUsername().equals(principal.getUsername())) { 94 | isOwner = true; 95 | } 96 | } 97 | 98 | if (!isOwner) { 99 | return ResponseEntity.ok().body(new Response(false, "没有操作权限")); 100 | } 101 | 102 | try { 103 | blogService.removeComment(blogId, id); 104 | commentService.removeComment(id); 105 | } catch (ConstraintViolationException e) { 106 | return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e))); 107 | } catch (Exception e) { 108 | return ResponseEntity.ok().body(new Response(false, e.getMessage())); 109 | } 110 | 111 | return ResponseEntity.ok().body(new Response(true, "处理成功", null)); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/HelloController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | /** 7 | * Hello World 控制器. 8 | */ 9 | @RestController 10 | public class HelloController { 11 | 12 | @RequestMapping("/hello") 13 | public String hello() { 14 | return "Hello World! Welcome to visit waylau.com!"; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/MainController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import com.pjb.blog.domain.Authority; 4 | import com.pjb.blog.domain.User; 5 | import com.pjb.blog.service.AuthorityService; 6 | import com.pjb.blog.service.UserService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.ui.Model; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * 主页控制器. 18 | */ 19 | @Controller 20 | public class MainController { 21 | private static final Long ROLE_USER_AUTHORITY_ID = 2L; 22 | private final UserService userService; 23 | private final AuthorityService authorityService; 24 | 25 | @Autowired 26 | public MainController(UserService userService, AuthorityService authorityService) { 27 | this.userService = userService; 28 | this.authorityService = authorityService; 29 | } 30 | 31 | @GetMapping("/") 32 | public String root() { 33 | return "redirect:index"; 34 | } 35 | 36 | @GetMapping("/index") 37 | public String index() { 38 | return "redirect:blogs"; 39 | } 40 | /** 41 | * 获取登录界面 42 | */ 43 | @GetMapping("/login") 44 | public String login() { 45 | return "login"; 46 | } 47 | 48 | @GetMapping("/login-error") 49 | public String loginError(Model model) { 50 | model.addAttribute("loginError", true); 51 | model.addAttribute("errorMsg", "登陆失败,账号或者密码错误!"); 52 | return "login"; 53 | } 54 | 55 | @GetMapping("/register") 56 | public String register() { 57 | return "register"; 58 | } 59 | 60 | /** 61 | * 注册用户 62 | */ 63 | @PostMapping("/register") 64 | public String registerUser(User user) { 65 | List authorities = new ArrayList<>(); 66 | authorities.add(authorityService.getAuthorityById(ROLE_USER_AUTHORITY_ID)); 67 | user.setAuthorities(authorities); 68 | user.setEncodePassword(user.getPassword()); // 加密密码 69 | userService.saveUser(user); 70 | return "redirect:/login"; 71 | } 72 | 73 | @GetMapping("/search") 74 | public String search() { 75 | return "search"; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import com.pjb.blog.domain.Authority; 4 | import com.pjb.blog.domain.User; 5 | import com.pjb.blog.service.AuthorityService; 6 | import com.pjb.blog.service.UserService; 7 | import com.pjb.blog.util.ConstraintViolationExceptionHandler; 8 | import com.pjb.blog.vo.Response; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.data.domain.Page; 11 | import org.springframework.data.domain.PageRequest; 12 | import org.springframework.data.domain.Pageable; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.security.access.prepost.PreAuthorize; 15 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 16 | import org.springframework.security.crypto.password.PasswordEncoder; 17 | import org.springframework.ui.Model; 18 | import org.springframework.web.bind.annotation.*; 19 | import org.springframework.web.servlet.ModelAndView; 20 | 21 | import javax.validation.ConstraintViolationException; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | 26 | /** 27 | * 用户控制器. 28 | */ 29 | @RestController 30 | @RequestMapping("/users") 31 | @PreAuthorize("hasAuthority('ROLE_ADMIN')") // 指定角色权限才能操作方法 32 | public class UserController { 33 | private final UserService userService; 34 | private final AuthorityService authorityService; 35 | 36 | @Autowired 37 | public UserController(UserService userService, AuthorityService authorityService) { 38 | this.userService = userService; 39 | this.authorityService = authorityService; 40 | } 41 | 42 | /** 43 | * 查询所用用户 44 | */ 45 | @GetMapping 46 | public ModelAndView list(@RequestParam(value="async",required=false) boolean async, 47 | @RequestParam(value="pageIndex",required=false,defaultValue="0") int pageIndex, 48 | @RequestParam(value="pageSize",required=false,defaultValue="10") int pageSize, 49 | @RequestParam(value="name",required=false,defaultValue="") String name, 50 | Model model) { 51 | 52 | Pageable pageable = PageRequest.of(pageIndex, pageSize); 53 | Page page = userService.listUsersByNameLike(name, pageable); 54 | List list = page.getContent(); // 当前所在页面数据列表 55 | 56 | model.addAttribute("page", page); 57 | model.addAttribute("userList", list); 58 | return new ModelAndView(async ?"users/list :: #mainContainerRepleace":"users/list", "userModel", model); 59 | } 60 | 61 | /** 62 | * 获取 form 表单页面 63 | */ 64 | @GetMapping("/add") 65 | public ModelAndView createForm(Model model) { 66 | model.addAttribute("user", new User(null, null, null, null)); 67 | return new ModelAndView("users/add", "userModel", model); 68 | } 69 | 70 | /** 71 | * 新建用户 72 | */ 73 | @PostMapping 74 | public ResponseEntity create(User user, Long authorityId) { 75 | List authorities = new ArrayList<>(); 76 | authorities.add(authorityService.getAuthorityById(authorityId)); 77 | user.setAuthorities(authorities); 78 | 79 | if(user.getId() == null) { 80 | user.setEncodePassword(user.getPassword()); // 加密密码 81 | }else { 82 | // 判断密码是否做了变更 83 | User originalUser = userService.getUserById(user.getId()); 84 | String rawPassword = originalUser.getPassword(); 85 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 86 | String encodePasswd = encoder.encode(user.getPassword()); 87 | boolean isMatch = encoder.matches(rawPassword, encodePasswd); 88 | if (!isMatch) { 89 | user.setEncodePassword(user.getPassword()); 90 | }else { 91 | user.setPassword(user.getPassword()); 92 | } 93 | } 94 | 95 | try { 96 | userService.saveUser(user); 97 | } catch (ConstraintViolationException e) { 98 | return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e))); 99 | } 100 | return ResponseEntity.ok().body(new Response(true, "处理成功", user)); 101 | } 102 | 103 | /** 104 | * 删除用户 105 | */ 106 | @DeleteMapping(value = "/{id}") 107 | public ResponseEntity delete(@PathVariable("id") Long id) { 108 | try { 109 | userService.removeUser(id); 110 | } catch (Exception e) { 111 | return ResponseEntity.ok().body( new Response(false, e.getMessage())); 112 | } 113 | return ResponseEntity.ok().body( new Response(true, "处理成功")); 114 | } 115 | 116 | /** 117 | * 获取修改用户的界面,及数据 118 | */ 119 | @GetMapping(value = "edit/{id}") 120 | public ModelAndView modifyForm(@PathVariable("id") Long id, Model model) { 121 | User user = userService.getUserById(id); 122 | model.addAttribute("user", user); 123 | return new ModelAndView("users/edit", "userModel", model); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/controller/VoteController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.controller; 2 | 3 | import com.pjb.blog.domain.User; 4 | import com.pjb.blog.service.BlogService; 5 | import com.pjb.blog.service.VoteService; 6 | import com.pjb.blog.util.ConstraintViolationExceptionHandler; 7 | import com.pjb.blog.vo.Response; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.security.access.prepost.PreAuthorize; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.web.bind.annotation.DeleteMapping; 14 | import org.springframework.web.bind.annotation.PathVariable; 15 | import org.springframework.web.bind.annotation.PostMapping; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | 18 | import javax.validation.ConstraintViolationException; 19 | 20 | 21 | /** 22 | * 点赞控制器. 23 | */ 24 | @Controller 25 | @RequestMapping("/votes") 26 | public class VoteController { 27 | private final BlogService blogService; 28 | private final VoteService voteService; 29 | 30 | @Autowired 31 | public VoteController(BlogService blogService, VoteService voteService) { 32 | this.blogService = blogService; 33 | this.voteService = voteService; 34 | } 35 | 36 | /** 37 | * 发表点赞 38 | */ 39 | @PostMapping 40 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN','ROLE_USER')") // 指定角色权限才能操作方法 41 | public ResponseEntity createVote(Long blogId) { 42 | 43 | try { 44 | blogService.createVote(blogId); 45 | } catch (ConstraintViolationException e) { 46 | return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e))); 47 | } catch (Exception e) { 48 | return ResponseEntity.ok().body(new Response(false, e.getMessage())); 49 | } 50 | 51 | return ResponseEntity.ok().body(new Response(true, "点赞成功", null)); 52 | } 53 | 54 | /** 55 | * 删除点赞 56 | */ 57 | @DeleteMapping("/{id}") 58 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN','ROLE_USER')") // 指定角色权限才能操作方法 59 | public ResponseEntity delete(@PathVariable("id") Long id, Long blogId) { 60 | 61 | boolean isOwner = false; 62 | User user = voteService.getVoteById(id).getUser(); 63 | 64 | // 判断操作用户是否是点赞的所有者 65 | if (SecurityContextHolder.getContext().getAuthentication() !=null && SecurityContextHolder.getContext().getAuthentication().isAuthenticated() 66 | && !"anonymousUser".equals(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString())) { 67 | User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 68 | if (principal !=null && user.getUsername().equals(principal.getUsername())) { 69 | isOwner = true; 70 | } 71 | } 72 | 73 | if (!isOwner) { 74 | return ResponseEntity.ok().body(new Response(false, "没有操作权限")); 75 | } 76 | 77 | try { 78 | blogService.removeVote(blogId, id); 79 | voteService.removeVote(id); 80 | } catch (ConstraintViolationException e) { 81 | return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e))); 82 | } catch (Exception e) { 83 | return ResponseEntity.ok().body(new Response(false, e.getMessage())); 84 | } 85 | 86 | return ResponseEntity.ok().body(new Response(true, "取消点赞成功", null)); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/domain/Authority.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.domain; 2 | 3 | import org.springframework.security.core.GrantedAuthority; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * 权限. 9 | */ 10 | @Entity 11 | public class Authority implements GrantedAuthority { 12 | @Id // 主键 13 | @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略 14 | private Long id; // 用户的唯一标识 15 | 16 | @Column(nullable = false) // 映射为字段,值不能为空 17 | private String name; 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | @Override 28 | public String getAuthority() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/domain/Catalog.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.persistence.*; 8 | import javax.validation.constraints.NotEmpty; 9 | import javax.validation.constraints.Size; 10 | import java.io.Serializable; 11 | 12 | /** 13 | * Catalog 实体 14 | */ 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @Entity 19 | public class Catalog implements Serializable { 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略 22 | private Long id; // 用户的唯一标识 23 | 24 | @NotEmpty(message = "名称不能为空") 25 | @Size(min=2, max=30) 26 | @Column(nullable = false) // 映射为字段,值不能为空 27 | private String name; 28 | 29 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY) 30 | @JoinColumn(name="user_id") 31 | private User user; 32 | 33 | 34 | public Catalog(User user, String name) { 35 | this.name = name; 36 | this.user = user; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/domain/Comment.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.persistence.*; 8 | import javax.validation.constraints.NotEmpty; 9 | import javax.validation.constraints.Size; 10 | import java.io.Serializable; 11 | import java.sql.Timestamp; 12 | 13 | /** 14 | * Comment 实体 15 | */ 16 | 17 | @Data 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | @Entity // 实体 21 | public class Comment implements Serializable { 22 | private static final long serialVersionUID = 1L; 23 | 24 | @Id // 主键 25 | @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略 26 | private Long id; // 用户的唯一标识 27 | 28 | @NotEmpty(message = "评论内容不能为空") 29 | @Size(min=2, max=500) 30 | @Column(nullable = false) // 映射为字段,值不能为空 31 | private String content; 32 | 33 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY) 34 | @JoinColumn(name="user_id") 35 | private User user; 36 | 37 | @Column(nullable = false) // 映射为字段,值不能为空 38 | @org.hibernate.annotations.CreationTimestamp // 由数据库自动创建时间 39 | private Timestamp createTime; 40 | 41 | public Comment(User user, String content) { 42 | this.content = content; 43 | this.user = user; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.domain; 2 | 3 | import org.hibernate.annotations.Proxy; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | 10 | import javax.persistence.*; 11 | import javax.validation.constraints.Email; 12 | import javax.validation.constraints.NotEmpty; 13 | import javax.validation.constraints.Size; 14 | import java.io.Serializable; 15 | import java.util.ArrayList; 16 | import java.util.Collection; 17 | import java.util.List; 18 | 19 | /** 20 | * User 实体 21 | */ 22 | @Entity // 实体 23 | @Proxy(lazy = false) 24 | public class User implements UserDetails, Serializable { 25 | 26 | @Id // 主键 27 | @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略 28 | private Long id; // 用户的唯一标识 29 | 30 | @NotEmpty(message = "姓名不能为空") 31 | @Size(min=2, max=20) 32 | @Column(nullable = false, length = 20) // 映射为字段,值不能为空 33 | private String name; 34 | 35 | @NotEmpty(message = "邮箱不能为空") 36 | @Size(max=50) 37 | @Email(message= "邮箱格式不对" ) 38 | @Column(nullable = false, length = 50, unique = true) 39 | private String email; 40 | 41 | @NotEmpty(message = "账号不能为空") 42 | @Size(min=3, max=20) 43 | @Column(nullable = false, length = 20, unique = true) 44 | private String username; // 用户账号,用户登录时的唯一标识 45 | 46 | @NotEmpty(message = "密码不能为空") 47 | @Size(max=100) 48 | @Column(length = 100) 49 | private String password; // 登录时密码 50 | 51 | @Column(length = 200) 52 | private String avatar; // 头像图片地址 53 | 54 | @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) 55 | @JoinTable(name = "user_authority", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), 56 | inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id")) 57 | private List authorities; 58 | 59 | protected User() { // JPA 的规范要求无参构造函数;设为 protected 防止直接使用 60 | } 61 | 62 | public User(String name, String email,String username,String password) { 63 | this.name = name; 64 | this.email = email; 65 | this.username = username; 66 | this.password = password; 67 | } 68 | 69 | public Long getId() { 70 | return id; 71 | } 72 | 73 | public void setId(Long id) { 74 | this.id = id; 75 | } 76 | 77 | public String getName() { 78 | return name; 79 | } 80 | 81 | public void setName(String name) { 82 | this.name = name; 83 | } 84 | 85 | public String getEmail() { 86 | return email; 87 | } 88 | 89 | public void setEmail(String email) { 90 | this.email = email; 91 | } 92 | 93 | @Override 94 | public Collection getAuthorities() { 95 | // 需将 List 转成 List,否则前端拿不到角色列表名称 96 | List simpleAuthorities = new ArrayList<>(); 97 | for(GrantedAuthority authority : this.authorities){ 98 | simpleAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority())); 99 | } 100 | return simpleAuthorities; 101 | } 102 | 103 | public void setAuthorities(List authorities) { 104 | this.authorities = authorities; 105 | } 106 | 107 | @Override 108 | public String getUsername() { 109 | return username; 110 | } 111 | 112 | public void setUsername(String username) { 113 | this.username = username; 114 | } 115 | 116 | @Override 117 | public String getPassword() { 118 | return password; 119 | } 120 | 121 | public void setPassword(String password) { 122 | this.password = password; 123 | } 124 | 125 | public void setEncodePassword(String password) { 126 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 127 | this.password = encoder.encode(password); 128 | } 129 | 130 | public String getAvatar() { 131 | return avatar; 132 | } 133 | 134 | public void setAvatar(String avatar) { 135 | this.avatar = avatar; 136 | } 137 | 138 | @Override 139 | public boolean isAccountNonExpired() { 140 | return true; 141 | } 142 | 143 | @Override 144 | public boolean isAccountNonLocked() { 145 | return true; 146 | } 147 | 148 | @Override 149 | public boolean isCredentialsNonExpired() { 150 | return true; 151 | } 152 | 153 | @Override 154 | public boolean isEnabled() { 155 | return true; 156 | } 157 | 158 | @Override 159 | public String toString() { 160 | return String.format("User[id=%d, username='%s', name='%s', email='%s', password='%s']", id, username, name, email, 161 | password); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/domain/Vote.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.persistence.*; 8 | import java.io.Serializable; 9 | import java.sql.Timestamp; 10 | 11 | /** 12 | * Like 实体 13 | */ 14 | 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @Entity 19 | public class Vote implements Serializable { 20 | @Id // 主键 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略 22 | private Long id; // 用户的唯一标识 23 | 24 | @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY) 25 | @JoinColumn(name="user_id") 26 | private User user; 27 | 28 | @Column(nullable = false) // 映射为字段,值不能为空 29 | @org.hibernate.annotations.CreationTimestamp // 由数据库自动创建时间 30 | private Timestamp createTime; 31 | 32 | public Vote(User user) { 33 | this.user = user; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/domain/es/EsBlog.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.domain.es; 2 | 3 | import com.pjb.blog.domain.Blog; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.data.annotation.Id; 8 | import org.springframework.data.elasticsearch.annotations.Document; 9 | import org.springframework.data.elasticsearch.annotations.Field; 10 | import org.springframework.data.elasticsearch.annotations.FieldType; 11 | 12 | import javax.xml.bind.annotation.XmlRootElement; 13 | import java.io.Serializable; 14 | import java.sql.Timestamp; 15 | 16 | 17 | /** 18 | * Blog. 19 | */ 20 | @Data 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | @Document(indexName = "blog", type = "blog") 24 | @XmlRootElement // MediaType 转为 XML 25 | public class EsBlog implements Serializable { 26 | @Id // 主键 27 | private String id; 28 | @Field(type = FieldType.Keyword) 29 | private Long blogId; // Blog 的 id 30 | private String title; 31 | private String summary; 32 | private String content; 33 | 34 | @Field(type = FieldType.Text,fielddata = true) // 不做全文检索字段 35 | private String username; 36 | @Field(type = FieldType.Text) // 不做全文检索字段 37 | private String avatar; 38 | @Field(type = FieldType.Text) // 不做全文检索字段 39 | private Timestamp createTime; 40 | @Field(type = FieldType.Text) // 不做全文检索字段 41 | private Integer readSize = 0; // 访问量、阅读量 42 | @Field(type = FieldType.Text) // 不做全文检索字段 43 | private Integer commentSize = 0; // 评论量 44 | @Field(type = FieldType.Text) // 不做全文检索字段 45 | private Integer voteSize = 0; // 点赞量 46 | @Field(type = FieldType.Text,fielddata = true) 47 | private String tags; // 标签 48 | 49 | 50 | public EsBlog(Blog blog){ 51 | this.blogId = blog.getId(); 52 | this.title = blog.getTitle(); 53 | this.summary = blog.getSummary(); 54 | this.content = blog.getContent(); 55 | this.username = blog.getUser().getUsername(); 56 | this.avatar = blog.getUser().getAvatar(); 57 | this.createTime = blog.getCreateTime(); 58 | this.readSize = blog.getReadSize(); 59 | this.commentSize = blog.getCommentSize(); 60 | this.voteSize = blog.getVoteSize(); 61 | this.tags = blog.getTags(); 62 | } 63 | 64 | public void update(Blog blog){ 65 | this.blogId = blog.getId(); 66 | this.title = blog.getTitle(); 67 | this.summary = blog.getSummary(); 68 | this.content = blog.getContent(); 69 | this.username = blog.getUser().getUsername(); 70 | this.avatar = blog.getUser().getAvatar(); 71 | this.createTime = blog.getCreateTime(); 72 | this.readSize = blog.getReadSize(); 73 | this.commentSize = blog.getCommentSize(); 74 | this.voteSize = blog.getVoteSize(); 75 | this.tags = blog.getTags(); 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return String.format( 81 | "User[id=%d, title='%s', content='%s']", 82 | blogId, title, content); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/repository/AuthorityRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.repository; 2 | 3 | import com.pjb.blog.domain.Authority; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | /** 7 | * Authority 仓库. 8 | */ 9 | public interface AuthorityRepository extends JpaRepository{ 10 | } 11 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/repository/BlogRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.repository; 2 | 3 | import com.pjb.blog.domain.Blog; 4 | import com.pjb.blog.domain.Catalog; 5 | import com.pjb.blog.domain.User; 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.Pageable; 8 | import org.springframework.data.jpa.repository.JpaRepository; 9 | 10 | /** 11 | * Blog 仓库. 12 | */ 13 | public interface BlogRepository extends JpaRepository{ 14 | /** 15 | * 根据用户名分页查询用户列表 16 | */ 17 | Page findByUserAndTitleLike(User user, String title, Pageable pageable); 18 | 19 | /** 20 | * 根据用户名分页查询用户列表 21 | */ 22 | Page findByTitleLikeAndUserOrTagsLikeAndUserOrderByCreateTimeDesc(String title, User user, String tags, User user2, Pageable pageable); 23 | /** 24 | * 根据用户名分页查询用户列表 25 | */ 26 | Page findByCatalog(Catalog catalog, Pageable pageable); 27 | } 28 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/repository/CatalogRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.repository; 2 | 3 | import com.pjb.blog.domain.Catalog; 4 | import com.pjb.blog.domain.User; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Catalog 仓库. 11 | */ 12 | public interface CatalogRepository extends JpaRepository{ 13 | 14 | /** 15 | * 根据用户查询 16 | */ 17 | List findByUser(User user); 18 | 19 | /** 20 | * 根据用户查询 21 | */ 22 | List findByUserAndName(User user, String name); 23 | } 24 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/repository/CommentRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.repository; 2 | 3 | import com.pjb.blog.domain.Comment; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | /** 7 | * Comment 仓库. 8 | */ 9 | public interface CommentRepository extends JpaRepository{ 10 | 11 | } 12 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.repository; 2 | 3 | import com.pjb.blog.domain.User; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | /** 12 | * 用户仓库. 13 | */ 14 | public interface UserRepository extends JpaRepository{ 15 | 16 | /** 17 | * 根据用户名分页查询用户列表 18 | */ 19 | Page findByNameLike(String name, Pageable pageable); 20 | /** 21 | * 根据名称查询 22 | */ 23 | User findByUsername(String username); 24 | /** 25 | * 根据名称列表查询 26 | */ 27 | List findByUsernameIn(Collection usernameCollection); 28 | } 29 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/repository/VoteRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.repository; 2 | 3 | import com.pjb.blog.domain.Vote; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | /** 7 | * Vote 仓库. 8 | */ 9 | public interface VoteRepository extends JpaRepository{ 10 | 11 | } 12 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/repository/es/EsBlogRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.repository.es; 2 | 3 | import com.pjb.blog.domain.es.EsBlog; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; 7 | 8 | /** 9 | * Blog 存储库. 10 | */ 11 | public interface EsBlogRepository extends ElasticsearchRepository { 12 | 13 | /** 14 | * 模糊查询(去重) 15 | */ 16 | Page findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContainingOrTagsContaining(String title, String summary, String content, String tags, Pageable pageable); 17 | 18 | EsBlog findByBlogId(Long blogId); 19 | } 20 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/AuthorityService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Authority; 4 | 5 | /** 6 | * Authority 服务接口. 7 | */ 8 | public interface AuthorityService { 9 | 10 | 11 | /** 12 | * 根据id获取 Authority 13 | */ 14 | Authority getAuthorityById(Long id); 15 | } 16 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/AuthorityServiceImpl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.pjb.blog.service; 5 | 6 | import com.pjb.blog.domain.Authority; 7 | import com.pjb.blog.repository.AuthorityRepository; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * Authority 服务. 13 | */ 14 | @Service 15 | public class AuthorityServiceImpl implements AuthorityService { 16 | 17 | private final AuthorityRepository authorityRepository; 18 | 19 | @Autowired 20 | public AuthorityServiceImpl(AuthorityRepository authorityRepository) { 21 | this.authorityRepository = authorityRepository; 22 | } 23 | 24 | @Override 25 | public Authority getAuthorityById(Long id) { 26 | return authorityRepository.findById(id).orElse(null); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/BlogService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Blog; 4 | import com.pjb.blog.domain.Catalog; 5 | import com.pjb.blog.domain.User; 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.Pageable; 8 | 9 | /** 10 | * Blog 服务接口. 11 | */ 12 | public interface BlogService { 13 | /** 14 | * 保存Blog 15 | */ 16 | void saveBlog(Blog blog); 17 | 18 | /** 19 | * 删除Blog 20 | */ 21 | void removeBlog(Long id); 22 | 23 | /** 24 | * 根据id获取Blog 25 | */ 26 | Blog getBlogById(Long id); 27 | 28 | /** 29 | * 根据用户名进行分页模糊查询(最新) 30 | */ 31 | Page listBlogsByTitleVote(User user, String title, Pageable pageable); 32 | 33 | /** 34 | * 根据用户名进行分页模糊查询(最热) 35 | */ 36 | Page listBlogsByTitleVoteAndSort(User suser, String title, Pageable pageable); 37 | 38 | /** 39 | * 根据分类进行查询 40 | */ 41 | Page listBlogsByCatalog(Catalog catalog, Pageable pageable); 42 | /** 43 | * 阅读量递增 44 | */ 45 | void readingIncrease(Long id); 46 | 47 | /** 48 | * 发表评论 49 | */ 50 | void createComment(Long blogId, String commentContent); 51 | 52 | /** 53 | * 删除评论 54 | */ 55 | void removeComment(Long blogId, Long commentId); 56 | 57 | /** 58 | * 点赞 59 | */ 60 | void createVote(Long blogId); 61 | 62 | /** 63 | * 取消点赞 64 | */ 65 | void removeVote(Long blogId, Long voteId); 66 | } 67 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/BlogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.*; 4 | import com.pjb.blog.domain.es.EsBlog; 5 | import com.pjb.blog.repository.BlogRepository; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.domain.Page; 8 | import org.springframework.data.domain.Pageable; 9 | import org.springframework.security.core.context.SecurityContextHolder; 10 | import org.springframework.stereotype.Service; 11 | 12 | import javax.transaction.Transactional; 13 | 14 | /** 15 | * Blog 服务. 16 | */ 17 | @Service 18 | public class BlogServiceImpl implements BlogService { 19 | 20 | private final BlogRepository blogRepository; 21 | private final EsBlogService esBlogService; 22 | 23 | @Autowired 24 | public BlogServiceImpl(BlogRepository blogRepository, EsBlogService esBlogService) { 25 | this.blogRepository = blogRepository; 26 | this.esBlogService = esBlogService; 27 | } 28 | 29 | @Transactional 30 | @Override 31 | public void saveBlog(Blog blog) { 32 | boolean isNew = (blog.getId() == null); 33 | EsBlog esBlog; 34 | 35 | Blog returnBlog = blogRepository.save(blog); 36 | 37 | if (isNew) { 38 | esBlog = new EsBlog(returnBlog); 39 | } else { 40 | esBlog = esBlogService.getEsBlogByBlogId(blog.getId()); 41 | esBlog.update(returnBlog); 42 | } 43 | 44 | esBlogService.updateEsBlog(esBlog); 45 | } 46 | 47 | @Transactional 48 | @Override 49 | public void removeBlog(Long id) { 50 | blogRepository.deleteById(id); 51 | EsBlog esblog = esBlogService.getEsBlogByBlogId(id); 52 | esBlogService.removeEsBlog(esblog.getId()); 53 | } 54 | 55 | @Override 56 | public Blog getBlogById(Long id) { 57 | return blogRepository.findById(id).orElse(null); 58 | } 59 | 60 | @Override 61 | public Page listBlogsByTitleVote(User user, String title, Pageable pageable) { 62 | // 模糊查询 63 | title = "%" + title + "%"; 64 | String tags = title; 65 | return blogRepository.findByTitleLikeAndUserOrTagsLikeAndUserOrderByCreateTimeDesc(title,user, tags,user, pageable); 66 | } 67 | 68 | @Override 69 | public Page listBlogsByTitleVoteAndSort(User user, String title, Pageable pageable) { 70 | // 模糊查询 71 | title = "%" + title + "%"; 72 | return blogRepository.findByUserAndTitleLike(user, title, pageable); 73 | } 74 | 75 | @Override 76 | public Page listBlogsByCatalog(Catalog catalog, Pageable pageable) { 77 | return blogRepository.findByCatalog(catalog, pageable); 78 | } 79 | 80 | @Override 81 | @Transactional 82 | public void readingIncrease(Long id) { 83 | Blog blog = blogRepository.findById(id).orElse(null); 84 | if (blog != null) { 85 | blog.setReadSize(blog.getCommentSize()+1); 86 | } 87 | this.saveBlog(blog); 88 | } 89 | 90 | @Override 91 | @Transactional 92 | public void createComment(Long blogId, String commentContent) { 93 | Blog originalBlog = blogRepository.findById(blogId).orElse(null); 94 | User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 95 | Comment comment = new Comment(user, commentContent); 96 | if (originalBlog != null) { 97 | originalBlog.addComment(comment); 98 | } 99 | this.saveBlog(originalBlog); 100 | } 101 | 102 | @Override 103 | @Transactional 104 | public void removeComment(Long blogId, Long commentId) { 105 | Blog originalBlog = blogRepository.findById(blogId).orElse(null); 106 | if (originalBlog != null) { 107 | originalBlog.removeComment(commentId); 108 | } 109 | this.saveBlog(originalBlog); 110 | } 111 | 112 | @Override 113 | @Transactional 114 | public void createVote(Long blogId) { 115 | Blog originalBlog = blogRepository.findById(blogId).orElse(null); 116 | User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 117 | Vote vote = new Vote(user); 118 | boolean isExist = false; 119 | if (originalBlog != null) { 120 | isExist = originalBlog.addVote(vote); 121 | } 122 | if (isExist) { 123 | throw new IllegalArgumentException("该用户已经点过赞了"); 124 | } 125 | this.saveBlog(originalBlog); 126 | } 127 | 128 | @Override 129 | @Transactional 130 | public void removeVote(Long blogId, Long voteId) { 131 | Blog originalBlog = blogRepository.findById(blogId).orElse(null); 132 | if (originalBlog != null) { 133 | originalBlog.removeVote(voteId); 134 | } 135 | this.saveBlog(originalBlog); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/CatalogService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Catalog; 4 | import com.pjb.blog.domain.User; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Catalog 服务接口. 10 | */ 11 | public interface CatalogService { 12 | /** 13 | * 保存Catalog 14 | */ 15 | void saveCatalog(Catalog catalog); 16 | 17 | /** 18 | * 删除Catalog 19 | */ 20 | void removeCatalog(Long id); 21 | 22 | /** 23 | * 根据id获取Catalog 24 | */ 25 | Catalog getCatalogById(Long id); 26 | 27 | /** 28 | * 获取Catalog列表 29 | */ 30 | List listCatalogs(User user); 31 | } 32 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/CatalogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Catalog; 4 | import com.pjb.blog.domain.User; 5 | import com.pjb.blog.repository.CatalogRepository; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Catalog 服务. 13 | */ 14 | @Service 15 | public class CatalogServiceImpl implements CatalogService{ 16 | 17 | private final CatalogRepository catalogRepository; 18 | 19 | @Autowired 20 | public CatalogServiceImpl(CatalogRepository catalogRepository) { 21 | this.catalogRepository = catalogRepository; 22 | } 23 | 24 | @Override 25 | public void saveCatalog(Catalog catalog) { 26 | // 判断重复 27 | List list = catalogRepository.findByUserAndName(catalog.getUser(), catalog.getName()); 28 | if(list !=null && !list.isEmpty()) { 29 | throw new IllegalArgumentException("该分类已经存在了"); 30 | } 31 | catalogRepository.save(catalog); 32 | } 33 | 34 | @Override 35 | public void removeCatalog(Long id) { 36 | catalogRepository.deleteById(id); 37 | } 38 | 39 | @Override 40 | public Catalog getCatalogById(Long id) { 41 | return catalogRepository.findById(id).orElse(null); 42 | } 43 | 44 | @Override 45 | public List listCatalogs(User user) { 46 | return catalogRepository.findByUser(user); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Comment; 4 | 5 | /** 6 | * Comment 服务接口. 7 | */ 8 | public interface CommentService { 9 | /** 10 | * 根据id获取 Comment 11 | */ 12 | Comment getCommentById(Long id); 13 | /** 14 | * 删除评论 15 | */ 16 | void removeComment(Long id); 17 | } 18 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/CommentServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Comment; 4 | import com.pjb.blog.repository.CommentRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import javax.transaction.Transactional; 9 | 10 | /** 11 | * Comment 服务. 12 | */ 13 | @Service 14 | public class CommentServiceImpl implements CommentService { 15 | 16 | private final CommentRepository commentRepository; 17 | 18 | @Autowired 19 | public CommentServiceImpl(CommentRepository commentRepository) { 20 | this.commentRepository = commentRepository; 21 | } 22 | 23 | @Override 24 | @Transactional 25 | public void removeComment(Long id) { 26 | commentRepository.deleteById(id); 27 | } 28 | @Override 29 | public Comment getCommentById(Long id) { 30 | return commentRepository.findById(id).orElse(null); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/EsBlogService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | 4 | import com.pjb.blog.domain.User; 5 | import com.pjb.blog.domain.es.EsBlog; 6 | import com.pjb.blog.vo.TagVO; 7 | import org.springframework.data.domain.Page; 8 | import org.springframework.data.domain.Pageable; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Blog 服务接口. 14 | */ 15 | public interface EsBlogService { 16 | 17 | /** 18 | * 删除Blog 19 | */ 20 | void removeEsBlog(String id); 21 | 22 | /** 23 | * 更新 EsBlog 24 | */ 25 | void updateEsBlog(EsBlog esBlog); 26 | 27 | /** 28 | * 根据id获取Blog 29 | */ 30 | EsBlog getEsBlogByBlogId(Long blogId); 31 | 32 | /** 33 | * 最新博客列表,分页 34 | */ 35 | Page listNewestEsBlogs(String keyword, Pageable pageable); 36 | 37 | /** 38 | * 最热博客列表,分页 39 | */ 40 | Page listHotestEsBlogs(String keyword, Pageable pageable); 41 | 42 | /** 43 | * 博客列表,分页 44 | */ 45 | Page listEsBlogs(Pageable pageable); 46 | /** 47 | * 最新前5 48 | */ 49 | List listTop5NewestEsBlogs(); 50 | 51 | /** 52 | * 最热前5 53 | */ 54 | List listTop5HotestEsBlogs(); 55 | 56 | /** 57 | * 最热前 30 标签 58 | */ 59 | List listTop30Tags(); 60 | 61 | /** 62 | * 最热前12用户 63 | */ 64 | List listTop12Users(); 65 | } 66 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/EsBlogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.User; 4 | import com.pjb.blog.domain.es.EsBlog; 5 | import com.pjb.blog.repository.es.EsBlogRepository; 6 | import com.pjb.blog.vo.TagVO; 7 | import org.elasticsearch.action.search.SearchResponse; 8 | import org.elasticsearch.action.search.SearchType; 9 | import org.elasticsearch.search.aggregations.Aggregations; 10 | import org.elasticsearch.search.aggregations.BucketOrder; 11 | import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; 12 | import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.data.domain.Page; 15 | import org.springframework.data.domain.PageRequest; 16 | import org.springframework.data.domain.Pageable; 17 | import org.springframework.data.domain.Sort; 18 | import org.springframework.data.domain.Sort.Direction; 19 | import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; 20 | import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; 21 | import org.springframework.data.elasticsearch.core.query.SearchQuery; 22 | import org.springframework.stereotype.Service; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; 28 | import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; 29 | 30 | /** 31 | * EsBlog 服务. 32 | */ 33 | @Service("EsBlogService") 34 | public class EsBlogServiceImpl implements EsBlogService { 35 | private final EsBlogRepository esBlogRepository; 36 | private final ElasticsearchTemplate elasticsearchTemplate; 37 | private final UserService userService; 38 | 39 | private static final Pageable TOP_5_PAGEABLE = PageRequest.of(0, 5); 40 | private static final String EMPTY_KEYWORD = ""; 41 | 42 | @Autowired 43 | public EsBlogServiceImpl(EsBlogRepository esBlogRepository, ElasticsearchTemplate elasticsearchTemplate, UserService userService) { 44 | this.esBlogRepository = esBlogRepository; 45 | this.elasticsearchTemplate = elasticsearchTemplate; 46 | this.userService = userService; 47 | } 48 | @Override 49 | public void removeEsBlog(String id) { 50 | esBlogRepository.deleteById(id); 51 | } 52 | 53 | @Override 54 | public void updateEsBlog(EsBlog esBlog) { 55 | esBlogRepository.save(esBlog); 56 | } 57 | 58 | @Override 59 | public EsBlog getEsBlogByBlogId(Long blogId) { 60 | return esBlogRepository.findByBlogId(blogId); 61 | } 62 | 63 | @Override 64 | public Page listNewestEsBlogs(String keyword, Pageable pageable){ 65 | Sort sort = new Sort(Direction.DESC,"createTime"); 66 | return esBlogRepository.findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContainingOrTagsContaining(keyword,keyword,keyword,keyword, pageSort(pageable,sort)); 67 | } 68 | 69 | @Override 70 | public Page listHotestEsBlogs(String keyword, Pageable pageable){ 71 | Sort sort = new Sort(Direction.DESC,"readSize","commentSize","voteSize","createTime"); 72 | return esBlogRepository.findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContainingOrTagsContaining(keyword, keyword, keyword, keyword, pageSort(pageable,sort)); 73 | } 74 | 75 | private Pageable pageSort(Pageable pageable,Sort sort){ 76 | if (pageable.getSort() == null) { 77 | return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort); 78 | }else { 79 | return null; 80 | } 81 | } 82 | 83 | @Override 84 | public Page listEsBlogs(Pageable pageable) { 85 | return esBlogRepository.findAll(pageable); 86 | } 87 | 88 | 89 | /** 90 | * 最新前5 91 | */ 92 | @Override 93 | public List listTop5NewestEsBlogs() { 94 | Page page = this.listNewestEsBlogs(EMPTY_KEYWORD, TOP_5_PAGEABLE); 95 | return page.getContent(); 96 | } 97 | 98 | /** 99 | * 最热前5 100 | */ 101 | @Override 102 | public List listTop5HotestEsBlogs() { 103 | Page page = this.listHotestEsBlogs(EMPTY_KEYWORD, TOP_5_PAGEABLE); 104 | return page.getContent(); 105 | } 106 | 107 | @Override 108 | public List listTop30Tags() { 109 | 110 | List list = new ArrayList<>(); 111 | // given 112 | SearchQuery searchQuery = new NativeSearchQueryBuilder() 113 | //将搜索条件设置到构建中 114 | .withQuery(matchAllQuery()) 115 | //设置搜索类型 116 | .withSearchType(SearchType.QUERY_THEN_FETCH) 117 | //指定索引库和文档类型 118 | .withIndices("blog").withTypes("blog") 119 | .addAggregation(terms("tags").field("tags").order(BucketOrder.count(false)).size(30)) 120 | .build(); 121 | // when 122 | Aggregations aggregations = elasticsearchTemplate.query(searchQuery, SearchResponse::getAggregations); 123 | 124 | StringTerms modelTerms = (StringTerms)aggregations.asMap().get("tags"); 125 | 126 | for (Bucket actiontypeBucket : modelTerms.getBuckets()) { 127 | list.add(new TagVO(actiontypeBucket.getKey().toString(), 128 | actiontypeBucket.getDocCount())); 129 | } 130 | return list; 131 | } 132 | /** 133 | * 最热前12用户 134 | */ 135 | @Override 136 | public List listTop12Users() { 137 | 138 | List usernamelist = new ArrayList<>(); 139 | // given 140 | SearchQuery searchQuery = new NativeSearchQueryBuilder() 141 | .withQuery(matchAllQuery()) 142 | .withSearchType(SearchType.QUERY_THEN_FETCH) 143 | .withIndices("blog").withTypes("blog") 144 | .addAggregation(terms("users").field("username").order(BucketOrder.count(false)).size(12)) 145 | .build(); 146 | // when 147 | Aggregations aggregations = elasticsearchTemplate.query(searchQuery, SearchResponse::getAggregations); 148 | StringTerms modelTerms = (StringTerms)aggregations.asMap().get("users"); 149 | 150 | for (Bucket actiontypeBucket : modelTerms.getBuckets()) { 151 | String username = actiontypeBucket.getKey().toString(); 152 | usernamelist.add(username); 153 | } 154 | return userService.listUsersByUsernames(usernamelist); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.User; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | /** 11 | * User 服务接口. 12 | */ 13 | public interface UserService { 14 | /** 15 | * 保存用户 16 | */ 17 | User saveUser(User user); 18 | 19 | /** 20 | * 删除用户 21 | */ 22 | void removeUser(Long id); 23 | 24 | /** 25 | * 删除列表里面的用户 26 | */ 27 | void removeUsersInBatch(List users); 28 | 29 | /** 30 | * 更新用户 31 | */ 32 | User updateUser(User user); 33 | 34 | /** 35 | * 根据id获取用户 36 | */ 37 | User getUserById(Long id); 38 | 39 | /** 40 | * 获取用户列表 41 | */ 42 | List listUsers(); 43 | 44 | /** 45 | * 根据用户名进行分页模糊查询 46 | */ 47 | Page listUsersByNameLike(String name, Pageable pageable); 48 | 49 | /** 50 | * 更具名称列表查询 51 | */ 52 | List listUsersByUsernames(Collection usernames); 53 | } 54 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.User; 4 | import com.pjb.blog.repository.UserRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.Pageable; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.stereotype.Service; 11 | 12 | import javax.transaction.Transactional; 13 | import java.util.Collection; 14 | import java.util.List; 15 | 16 | /** 17 | * User 服务. 18 | */ 19 | @Service("UserService") 20 | public class UserServiceImpl implements UserService ,UserDetailsService { 21 | 22 | private final UserRepository userRepository; 23 | 24 | @Autowired 25 | public UserServiceImpl(UserRepository userRepository) { 26 | this.userRepository = userRepository; 27 | } 28 | 29 | @Transactional 30 | @Override 31 | public User saveUser(User user) { 32 | return userRepository.save(user); 33 | } 34 | 35 | @Transactional 36 | @Override 37 | public void removeUser(Long id) { 38 | userRepository.deleteById(id); 39 | } 40 | 41 | @Transactional 42 | @Override 43 | public void removeUsersInBatch(List users) { 44 | userRepository.deleteInBatch(users); 45 | } 46 | 47 | @Transactional 48 | @Override 49 | public User updateUser(User user) { 50 | return userRepository.save(user); 51 | } 52 | 53 | @Override 54 | public User getUserById(Long id) { 55 | return userRepository.getOne(id); 56 | } 57 | 58 | @Override 59 | public List listUsers() { 60 | return userRepository.findAll(); 61 | } 62 | 63 | @Override 64 | public Page listUsersByNameLike(String name, Pageable pageable) { 65 | // 模糊查询 66 | name = "%" + name + "%"; 67 | return userRepository.findByNameLike(name, pageable); 68 | } 69 | 70 | public UserDetails loadUserByUsername(String username){ 71 | return userRepository.findByUsername(username); 72 | } 73 | 74 | @Override 75 | public List listUsersByUsernames(Collection usernames) { 76 | return userRepository.findByUsernameIn(usernames); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/VoteService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Vote; 4 | 5 | /** 6 | * Vote 服务接口. 7 | */ 8 | public interface VoteService { 9 | /** 10 | * 根据id获取 Vote 11 | */ 12 | Vote getVoteById(Long id); 13 | /** 14 | * 删除Vote 15 | */ 16 | void removeVote(Long id); 17 | } 18 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/service/VoteServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.service; 2 | 3 | import com.pjb.blog.domain.Vote; 4 | import com.pjb.blog.repository.VoteRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import javax.transaction.Transactional; 9 | 10 | /** 11 | * Vote 服务. 12 | */ 13 | @Service 14 | public class VoteServiceImpl implements VoteService { 15 | 16 | private final VoteRepository voteRepository; 17 | 18 | @Autowired 19 | public VoteServiceImpl(VoteRepository voteRepository) { 20 | this.voteRepository = voteRepository; 21 | } 22 | 23 | @Override 24 | @Transactional 25 | public void removeVote(Long id) { 26 | voteRepository.deleteById(id); 27 | } 28 | @Override 29 | public Vote getVoteById(Long id) { 30 | return voteRepository.findById(id).orElse(null); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/util/ConstraintViolationExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.util; 2 | 3 | 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.ConstraintViolationException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class ConstraintViolationExceptionHandler { 12 | private ConstraintViolationExceptionHandler() { 13 | throw new IllegalStateException("Utility class"); 14 | } 15 | /** 16 | * 获取批量异常信息 17 | */ 18 | public static String getMessage(ConstraintViolationException e) { 19 | List msgList = new ArrayList<>(); 20 | for (ConstraintViolation constraintViolation : e.getConstraintViolations()) { 21 | msgList.add(constraintViolation.getMessage()); 22 | } 23 | return StringUtils.join(msgList.toArray(), ";"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/vo/CatalogVO.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.vo; 2 | 3 | 4 | import com.pjb.blog.domain.Catalog; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class CatalogVO implements Serializable { 15 | private String username; 16 | private Catalog catalog; 17 | } 18 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/vo/Menu.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 菜单 值对象 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class Menu implements Serializable{ 16 | private String name; 17 | private String url; 18 | } 19 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/vo/Response.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.vo; 2 | 3 | /** 4 | * 响应 值对象. 5 | */ 6 | 7 | public class Response { 8 | 9 | 10 | private boolean success; 11 | private String message; 12 | private Object body; 13 | 14 | /** 响应处理是否成功 */ 15 | public boolean isSuccess() { 16 | return success; 17 | } 18 | public void setSuccess(boolean success) { 19 | this.success = success; 20 | } 21 | 22 | /** 响应处理的消息 */ 23 | public String getMessage() { 24 | return message; 25 | } 26 | public void setMessage(String message) { 27 | this.message = message; 28 | } 29 | 30 | /** 响应处理的返回内容 */ 31 | public Object getBody() { 32 | return body; 33 | } 34 | public void setBody(Object body) { 35 | this.body = body; 36 | } 37 | 38 | public Response(boolean success, String message) { 39 | this.success = success; 40 | this.message = message; 41 | } 42 | 43 | public Response(boolean success, String message, Object body) { 44 | this.success = success; 45 | this.message = message; 46 | this.body = body; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /blog/src/main/java/com/pjb/blog/vo/TagVO.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * Tag 值对象. 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class TagVO implements Serializable { 16 | private String name; 17 | private Long count; 18 | } 19 | -------------------------------------------------------------------------------- /blog/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | thymeleaf: 3 | mode: HTML 4 | cache: false 5 | servlet: 6 | content-type: text/html 7 | datasource: 8 | driver-class-name: com.mysql.cj.jdbc.Driver 9 | url: jdbc:mysql://localhost/blog?useSSL=false&serverTimezone=UTC 10 | username: root 11 | password: 1997 12 | data: 13 | elasticsearch: 14 | cluster-nodes: localhost:9300 15 | cluster-name: my-application 16 | jpa: 17 | show-sql: true 18 | hibernate: 19 | ddl-auto: update 20 | open-in-view: false 21 | main: 22 | allow-bean-definition-overriding: true 23 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/blog.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Blog main style. 3 | * 4 | * @since: 1.0.0 5 | * @author Way Lau 6 | */ 7 | /* Footer */ 8 | .blog-footer { 9 | bottom: 0; 10 | width: 100%; 11 | height: 60px; 12 | line-height: 60px; /* Vertically center the text there */ 13 | } 14 | 15 | /* Index content*/ 16 | .blog-content-container { 17 | margin-top: 2.0em; 18 | background-color: #fff; 19 | } 20 | 21 | /* Index sidebar */ 22 | .blog-avatar-50 { 23 | border-radius: 50%; 24 | height: 50px; 25 | width: 50px; 26 | } 27 | 28 | /* User space */ 29 | .blog-avatar-100 { 30 | border-radius: 50%; 31 | height: 100px; 32 | width: 100px; 33 | } 34 | 35 | .blog-avatar-230 { 36 | border-radius: 10%; 37 | height: 230px; 38 | width: 230px; 39 | } 40 | 41 | /* blog commit */ 42 | .blog-textarea { 43 | border-radius: 0; 44 | margin-bottom: 10px; 45 | width: 100%; 46 | height: 80px; 47 | padding: 5px 10px; 48 | border: 1px solid #ddd; 49 | border-radius: 3px; 50 | resize: none; 51 | } 52 | 53 | .blog-label-success { 54 | color: #5cb85c; 55 | } 56 | 57 | .blog-label-error { 58 | color: #d9534f; 59 | } 60 | 61 | /* blog avatar */ 62 | .blog-avatar-img { 63 | max-width: 100%; 64 | } 65 | 66 | /* 元素靠右 */ 67 | .blog-right { 68 | float: right; 69 | margin-right:0px; 70 | } 71 | 72 | .blog-list-group-item { 73 | padding: .75rem 1.25rem; 74 | border: 1px solid rgba(0,0,0,.125); 75 | } 76 | 77 | 78 | /* 避免图片超出 */ 79 | article img { 80 | max-width: 100%; 81 | } 82 | 83 | 84 | /* go to top */ 85 | #goToTop{ 86 | display:block; 87 | width:40px; 88 | height:40px; 89 | position:fixed; 90 | bottom:60px; 91 | right:10px; 92 | text-decoration:none; 93 | } 94 | 95 | #goToTop span{ 96 | display:block; 97 | color:#dddddd; 98 | } 99 | 100 | #goToTop span:hover{ 101 | color:#cccccc; 102 | } 103 | 104 | #md { 105 | height:400px; 106 | } 107 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}@-ms-viewport{width:device-width}html{-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#292b2c;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{cursor:help}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#0275d8;text-decoration:none}a:focus,a:hover{color:#014c8c;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse;background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#636c72;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,select,textarea{line-height:inherit}input[type=checkbox]:disabled,input[type=radio]:disabled{cursor:not-allowed}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}/*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/_normalize.scss","bootstrap-reboot.css","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/mixins/_hover.scss"],"names":[],"mappings":"4EAYA,KACE,YAAA,WACA,YAAA,KACA,qBAAA,KACA,yBAAA,KAUF,KACE,OAAA,EAOF,QAAA,MAAA,OAAA,OAAA,IAAA,QAME,QAAA,MAQF,GACE,UAAA,IACA,OAAA,MAAA,EAWF,WAAA,OAAA,KAGE,QAAA,MAOF,OACE,OAAA,IAAA,KAQF,GACE,mBAAA,YAAA,WAAA,YACA,OAAA,EACA,SAAA,QAQF,IACE,YAAA,UAAA,UACA,UAAA,IAWF,EACE,iBAAA,YACA,6BAAA,QAQF,SAAA,QAEE,cAAA,EAQF,YACE,cAAA,KACA,gBAAA,UACA,gBAAA,UAAA,OAOF,EAAA,OAEE,YAAA,QAOF,EAAA,OAEE,YAAA,OAQF,KAAA,IAAA,KAGE,YAAA,UAAA,UACA,UAAA,IAOF,IACE,WAAA,OAOF,KACE,iBAAA,KACA,MAAA,KAOF,MACE,UAAA,IAQF,IAAA,IAEE,UAAA,IACA,YAAA,EACA,SAAA,SACA,eAAA,SAGF,IACE,OAAA,OAGF,IACE,IAAA,MAUF,MAAA,MAEE,QAAA,aAOF,sBACE,QAAA,KACA,OAAA,EAOF,IACE,aAAA,KAOF,eACE,SAAA,OAWF,OAAA,MAAA,SAAA,OAAA,SAKE,YAAA,WACA,UAAA,KACA,YAAA,KACA,OAAA,EAQF,OAAA,MAEE,SAAA,QAQF,OAAA,OAEE,eAAA,KASF,aAAA,cAAA,OAAA,mBAIE,mBAAA,OAOF,gCAAA,+BAAA,gCAAA,yBAIE,aAAA,KACA,QAAA,EAOF,6BAAA,4BAAA,6BAAA,sBAIE,QAAA,IAAA,OAAA,WAOF,SACE,OAAA,IAAA,MAAA,OACA,OAAA,EAAA,IACA,QAAA,MAAA,OAAA,MAUF,OACE,mBAAA,WAAA,WAAA,WACA,MAAA,QACA,QAAA,MACA,UAAA,KACA,QAAA,EACA,YAAA,OAQF,SACE,QAAA,aACA,eAAA,SAOF,SACE,SAAA,KCrKF,gBAAA,aD+KE,mBAAA,WAAA,WAAA,WACA,QAAA,EC1KF,yCAAA,yCDmLE,OAAA,KC9KF,cDuLE,mBAAA,UACA,eAAA,KCnLF,4CAAA,yCD4LE,mBAAA,KAQF,6BACE,mBAAA,OACA,KAAA,QAWF,QAAA,KAEE,QAAA,MAOF,QACE,QAAA,UAUF,OACE,QAAA,aAOF,SACE,QAAA,KCnNF,SD8NE,QAAA,KEtbF,KACE,mBAAA,WAAA,WAAA,WAGF,EAAA,QAAA,SAGE,mBAAA,QAAA,WAAA,QAoBA,cAAgB,MAAA,aAQlB,KAYE,mBAAA,UAGA,4BAAA,YAGF,KACE,YAAA,cAAA,UAAA,mBAAA,WAAA,OC2K4H,iBD3K5H,MAAA,WACA,UAAA,KACA,YAAA,IACA,YAAA,IAEA,MAAA,QAEA,iBAAA,KD2LF,sBClLE,QAAA,YAYF,GAAI,GAAI,GAAI,GAAI,GAAI,GAClB,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KAIF,0BAAA,YAGE,OAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAGF,GAAA,GAAA,GAGE,WAAA,EACA,cAAA,KAGF,MAAA,MAAA,MAAA,MAIE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAQF,EACE,MAAA,QACA,gBAAA,KEhJE,QAAA,QFmJA,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KEhKE,oCAAA,oCFmKA,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EASJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAGE,OAAA,EAAA,EAAA,KAQF,IAGE,eAAA,ODsIF,cCzHE,OAAA,QAcF,cAAA,EAAA,KAAA,OAAA,MAAA,MAAA,OAAA,QAAA,SASE,iBAAA,aAAA,aAAA,aAQF,MAEE,gBAAA,SAEA,iBAAA,YAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAEE,WAAA,KAQF,MAEE,QAAA,aACA,cAAA,MAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBAGF,OAAA,MAAA,OAAA,SAME,YAAA,QAGF,8BAAA,2BAMI,OAAA,YAKJ,iBAAA,iBAAA,2BAAA,kBASE,mBAAA,QAGF,SAEE,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAGF,OAEE,QAAA,MACA,MAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QAGF,mBAKE,mBAAA,KAIF,OACE,QAAA,aDsEF,SC9DE,QAAA"} -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/bootstrap-table.min.css: -------------------------------------------------------------------------------- 1 | .fixed-table-container .bs-checkbox,.fixed-table-container .no-records-found{text-align:center}.fixed-table-body thead th .th-inner,.table td,.table th{box-sizing:border-box}.bootstrap-table .table{margin-bottom:0!important;border-bottom:1px solid #ddd;border-collapse:collapse!important;border-radius:1px}.bootstrap-table .table:not(.table-condensed),.bootstrap-table .table:not(.table-condensed)>tbody>tr>td,.bootstrap-table .table:not(.table-condensed)>tbody>tr>th,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>td,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>th,.bootstrap-table .table:not(.table-condensed)>thead>tr>td{padding:8px}.bootstrap-table .table.table-no-bordered>tbody>tr>td,.bootstrap-table .table.table-no-bordered>thead>tr>th{border-right:2px solid transparent}.bootstrap-table .table.table-no-bordered>tbody>tr>td:last-child{border-right:none}.fixed-table-container{position:relative;clear:both;border:1px solid #ddd;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px}.fixed-table-container.table-no-bordered{border:1px solid transparent}.fixed-table-footer,.fixed-table-header{overflow:hidden}.fixed-table-footer{border-top:1px solid #ddd}.fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.fixed-table-container table{width:100%}.fixed-table-container thead th{height:0;padding:0;margin:0;border-left:1px solid #ddd}.fixed-table-container thead th:focus{outline:transparent solid 0}.fixed-table-container thead th:first-child{border-left:none;border-top-left-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px}.fixed-table-container tbody td .th-inner,.fixed-table-container thead th .th-inner{padding:8px;line-height:24px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fixed-table-container thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px}.fixed-table-container thead th .both{background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC')}.fixed-table-container thead th .asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==)}.fixed-table-container thead th .desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=)}.fixed-table-container th.detail{width:30px}.fixed-table-container tbody td{border-left:1px solid #ddd}.fixed-table-container tbody tr:first-child td{border-top:none}.fixed-table-container tbody td:first-child{border-left:none}.fixed-table-container tbody .selected td{background-color:#f5f5f5}.fixed-table-container .bs-checkbox .th-inner{padding:8px 0}.fixed-table-container input[type=radio],.fixed-table-container input[type=checkbox]{margin:0 auto!important}.fixed-table-pagination .pagination-detail,.fixed-table-pagination div.pagination{margin-top:10px;margin-bottom:10px}.fixed-table-pagination div.pagination .pagination{margin:0}.fixed-table-pagination .pagination a{padding:6px 12px;line-height:1.428571429}.fixed-table-pagination .pagination-info{line-height:34px;margin-right:5px}.fixed-table-pagination .btn-group{position:relative;display:inline-block;vertical-align:middle}.fixed-table-pagination .dropup .dropdown-menu{margin-bottom:0}.fixed-table-pagination .page-list{display:inline-block}.fixed-table-toolbar .columns-left{margin-right:5px}.fixed-table-toolbar .columns-right{margin-left:5px}.fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.fixed-table-toolbar .bs-bars,.fixed-table-toolbar .columns,.fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px;line-height:34px}.fixed-table-pagination li.disabled a{pointer-events:none;cursor:default}.fixed-table-loading{display:none;position:absolute;top:42px;right:0;bottom:0;left:0;z-index:99;background-color:#fff;text-align:center}.fixed-table-body .card-view .title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.table td,.table th{vertical-align:middle}.fixed-table-toolbar .dropdown-menu{text-align:left;max-height:300px;overflow:auto}.fixed-table-toolbar .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.fixed-table-toolbar .btn-group>.btn-group>.btn{border-radius:0}.fixed-table-toolbar .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.fixed-table-toolbar .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .table>thead>tr>th{vertical-align:bottom;border-bottom:1px solid #ddd}.bootstrap-table .table thead>tr>th{padding:0;margin:0}.bootstrap-table .fixed-table-footer tbody>tr>td{padding:0!important}.bootstrap-table .fixed-table-footer .table{border-bottom:none;border-radius:0;padding:0!important}.bootstrap-table .pull-right .dropdown-menu{right:0;left:auto}p.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}.fixed-table-pagination:after,.fixed-table-toolbar:after{content:"";display:block;clear:both} -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/cropbox.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * 图片剪切 style. 3 | * 4 | * @since: 1.0.0 5 | * @author Way Lau 6 | */ 7 | .crop { 8 | display: none; 9 | } 10 | 11 | .crop-input { 12 | visibility: hidden; 13 | width: 0; 14 | height: 0; 15 | } 16 | 17 | .crop-mask { 18 | position: fixed; 19 | left: 0; 20 | top: 0; 21 | width: 100%; 22 | height: 100%; 23 | background-color: rgba(0, 0, 0, .4); 24 | z-index: 59; 25 | } 26 | 27 | .crop-wrap { 28 | position: fixed; 29 | left: 5%; 30 | top: 50%; 31 | width: 90%; 32 | background-color: #fff; 33 | -webkit-transform: translate3d(0, -50%, 0); 34 | -moz-transform: translate3d(0, -50%, 0); 35 | -ms-transform: translate3d(0, -50%, 0); 36 | -o-transform: translate3d(0, -50%, 0); 37 | transform: translate3d(0, -50%, 0); 38 | z-index: 99; 39 | } 40 | 41 | .crop-wrap-content { 42 | position: relative; 43 | width: 250px; 44 | height: 250px; 45 | line-height: 250px; 46 | margin: 10px auto; 47 | } 48 | 49 | .crop-wrap-thum { 50 | position: absolute; 51 | /* LEFT: 20%; */ 52 | top: 50%; 53 | width: 100%; 54 | height: 100%; 55 | z-index: 99; 56 | /*background: none repeat scroll 0 0 transparent;*/ 57 | background: transparent; 58 | border: 1px solid rgb(102, 102, 102); 59 | -webkit-transform: translate3d(0, -50%, 0); 60 | -moz-transform: translate3d(0, -50%, 0); 61 | -ms-transform: translate3d(0, -50%, 0); 62 | -o-transform: translate3d(0, -50%, 0); 63 | transform: translate3d(0, -50%, 0); 64 | -webkit-box-shadow: 0 0 0 51px rgba(0, 0, 0, 0.4); 65 | -moz-box-shadow: 0 0 0 51px rgba(0, 0, 0, 0.4); 66 | box-shadow: 0 0 0 51px rgba(0, 0, 0, 0.4); 67 | } 68 | 69 | .crop-wrap-spinner { 70 | position: absolute; 71 | left: 0; 72 | top: 0; 73 | width: 100%; 74 | height: 100%; 75 | background-color: rgba(0, 0, 0, .6); 76 | color: #fff; 77 | text-align: center; 78 | display: none; 79 | } 80 | 81 | .crop-wrap-group { 82 | width: 100%; 83 | height: 30px; 84 | line-height: 30px; 85 | text-align: center; 86 | margin: 10px 0; 87 | } 88 | 89 | .crop-wrap-group > a { 90 | width: 40%; 91 | height: 100%; 92 | display: inline-block; 93 | background-color: #ff0000; 94 | color: #fff; 95 | } 96 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/images/emoji/Sysmbols.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/css/images/emoji/Sysmbols.png -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/images/emoji/nature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/css/images/emoji/nature.png -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/images/emoji/object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/css/images/emoji/object.png -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/images/emoji/people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/css/images/emoji/people.png -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/images/emoji/place.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/css/images/emoji/place.png -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/images/emoji/twemoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/css/images/emoji/twemoji.png -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/jquery.tagsinput.min.css: -------------------------------------------------------------------------------- 1 | div.tagsinput{border:1px solid #CCC;background:#FFF;padding:5px;width:300px;height:100px;overflow-y:auto}div.tagsinput span.tag{border:1px solid #a5d24a;-moz-border-radius:2px;-webkit-border-radius:2px;display:block;float:left;padding:5px;text-decoration:none;background:#cde69c;color:#638421;margin-right:5px;margin-bottom:5px;font-family:helvetica;font-size:13px}div.tagsinput span.tag a{font-weight:700;color:#82ad2b;text-decoration:none;font-size:11px}div.tagsinput input{width:80px;margin:0 5px 5px 0;font-family:helvetica;font-size:13px;border:1px solid transparent;padding:5px;background:0 0;color:#000;outline:0}div.tagsinput div{display:block;float:left}.tags_clear{clear:both;width:100%;height:0}.not_valid{background:#FBD8DB!important;color:#90111A!important} -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/nprogress.css: -------------------------------------------------------------------------------- 1 | /* Make clicks pass-through */ 2 | #nprogress { 3 | pointer-events: none; 4 | } 5 | 6 | #nprogress .bar { 7 | background: #29d; 8 | 9 | position: fixed; 10 | z-index: 1031; 11 | top: 0; 12 | left: 0; 13 | 14 | width: 100%; 15 | height: 2px; 16 | } 17 | 18 | /* Fancy blur effect */ 19 | #nprogress .peg { 20 | display: block; 21 | position: absolute; 22 | right: 0px; 23 | width: 100px; 24 | height: 100%; 25 | box-shadow: 0 0 10px #29d, 0 0 5px #29d; 26 | opacity: 1.0; 27 | 28 | -webkit-transform: rotate(3deg) translate(0px, -4px); 29 | -ms-transform: rotate(3deg) translate(0px, -4px); 30 | transform: rotate(3deg) translate(0px, -4px); 31 | } 32 | 33 | /* Remove these to get rid of the spinner */ 34 | #nprogress .spinner { 35 | display: block; 36 | position: fixed; 37 | z-index: 1031; 38 | top: 15px; 39 | right: 15px; 40 | } 41 | 42 | #nprogress .spinner-icon { 43 | width: 18px; 44 | height: 18px; 45 | box-sizing: border-box; 46 | 47 | border: solid 2px transparent; 48 | border-top-color: #29d; 49 | border-left-color: #29d; 50 | border-radius: 50%; 51 | 52 | -webkit-animation: nprogress-spinner 400ms linear infinite; 53 | animation: nprogress-spinner 400ms linear infinite; 54 | } 55 | 56 | .nprogress-custom-parent { 57 | overflow: hidden; 58 | position: relative; 59 | } 60 | 61 | .nprogress-custom-parent #nprogress .spinner, 62 | .nprogress-custom-parent #nprogress .bar { 63 | position: absolute; 64 | } 65 | 66 | @-webkit-keyframes nprogress-spinner { 67 | 0% { -webkit-transform: rotate(0deg); } 68 | 100% { -webkit-transform: rotate(360deg); } 69 | } 70 | @keyframes nprogress-spinner { 71 | 0% { transform: rotate(0deg); } 72 | 100% { transform: rotate(360deg); } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/style.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Override Bootstrap style. 3 | * 4 | * @since: 1.0.0 5 | * @author Way Lau 6 | */ 7 | 8 | .badge { 9 | margin-left: auto; 10 | } 11 | 12 | .hidden { 13 | display: none !important; 14 | } -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/tether-theme-arrows-dark.min.css: -------------------------------------------------------------------------------- 1 | .tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-center .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-middle .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-top .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-middle .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-top .tether-content{margin-bottom:16px}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-left .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-right.tether-element-attached-middle .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-right.tether-target-attached-left .tether-content{margin-right:16px}.tether-element,.tether-element *,.tether-element :after,.tether-element :before,.tether-element:after,.tether-element:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block}.tether-element.tether-theme-arrows-dark{max-width:100%;max-height:100%}.tether-element.tether-theme-arrows-dark .tether-content{border-radius:5px;position:relative;font-family:inherit;background:#000;color:#fff;padding:1em;font-size:1.1em;line-height:1.5em}.tether-element.tether-theme-arrows-dark .tether-content:before{content:"";display:block;position:absolute;width:0;height:0;border-color:transparent;border-width:16px;border-style:solid}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-center .tether-content:before{top:100%;left:50%;margin-left:-16px;border-top-color:#000;border-bottom:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-center .tether-content{margin-top:16px}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-center .tether-content:before{bottom:100%;left:50%;margin-left:-16px;border-bottom-color:#000;border-top:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-right .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-left.tether-element-attached-middle .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-right .tether-content{margin-left:16px}.tether-element.tether-theme-arrows-dark.tether-element-attached-right.tether-element-attached-middle .tether-content:before{left:100%;top:50%;margin-top:-16px;border-left-color:#000;border-right:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-left.tether-element-attached-middle .tether-content:before{right:100%;top:50%;margin-top:-16px;border-right-color:#000;border-left:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-bottom .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-middle .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-right.tether-target-attached-bottom .tether-content,.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-right.tether-target-attached-middle .tether-content{margin-top:16px}.tether-element.tether-theme-arrows-dark.tether-element-attached-left.tether-target-attached-center .tether-content{left:-32px}.tether-element.tether-theme-arrows-dark.tether-element-attached-right.tether-target-attached-center .tether-content{left:32px}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-middle .tether-content:before{bottom:100%;left:16px;border-bottom-color:#000;border-top:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-right.tether-target-attached-middle .tether-content:before{bottom:100%;right:16px;border-bottom-color:#000;border-top:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-middle .tether-content:before{top:100%;left:16px;border-top-color:#000;border-bottom:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-middle .tether-content:before{top:100%;right:16px;border-top-color:#000;border-bottom:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-bottom .tether-content:before{bottom:100%;left:16px;border-bottom-color:#000;border-top:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-right.tether-target-attached-bottom .tether-content:before{bottom:100%;right:16px;border-bottom-color:#000;border-top:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-top .tether-content:before{top:100%;left:16px;border-top-color:#000;border-bottom:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-top .tether-content:before{top:100%;right:16px;border-top-color:#000;border-bottom:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-right.tether-target-attached-left .tether-content:before{top:16px;left:100%;border-left-color:#000;border-right:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-right .tether-content:before{top:16px;right:100%;border-right-color:#000;border-left:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-left .tether-content:before{bottom:16px;left:100%;border-left-color:#000;border-right:0}.tether-element.tether-theme-arrows-dark.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-right .tether-content:before{bottom:16px;right:100%;border-right-color:#000;border-left:0} -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/tether-theme-arrows.min.css: -------------------------------------------------------------------------------- 1 | .tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-center .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-middle .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-top .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-middle .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-top .tether-content{margin-bottom:16px}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-left .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-right.tether-element-attached-middle .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-right.tether-target-attached-left .tether-content{margin-right:16px}.tether-element,.tether-element *,.tether-element :after,.tether-element :before,.tether-element:after,.tether-element:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block}.tether-element.tether-theme-arrows{max-width:100%;max-height:100%}.tether-element.tether-theme-arrows .tether-content{border-radius:5px;position:relative;font-family:inherit;background:#fff;color:inherit;padding:1em;font-size:1.1em;line-height:1.5em;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-filter:drop-shadow(0 1px 4px rgba(0, 0, 0, .2));filter:drop-shadow(0 1px 4px rgba(0, 0, 0, .2))}.tether-element.tether-theme-arrows .tether-content:before{content:"";display:block;position:absolute;width:0;height:0;border-color:transparent;border-width:16px;border-style:solid}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-center .tether-content:before{top:100%;left:50%;margin-left:-16px;border-top-color:#fff;border-bottom:0}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-center .tether-content{margin-top:16px}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-center .tether-content:before{bottom:100%;left:50%;margin-left:-16px;border-bottom-color:#fff;border-top:0}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-right .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-left.tether-element-attached-middle .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-left.tether-target-attached-right .tether-content{margin-left:16px}.tether-element.tether-theme-arrows.tether-element-attached-right.tether-element-attached-middle .tether-content:before{left:100%;top:50%;margin-top:-16px;border-left-color:#fff;border-right:0}.tether-element.tether-theme-arrows.tether-element-attached-left.tether-element-attached-middle .tether-content:before{right:100%;top:50%;margin-top:-16px;border-right-color:#fff;border-left:0}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-left.tether-target-attached-bottom .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-left.tether-target-attached-middle .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-right.tether-target-attached-bottom .tether-content,.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-right.tether-target-attached-middle .tether-content{margin-top:16px}.tether-element.tether-theme-arrows.tether-element-attached-left.tether-target-attached-center .tether-content{left:-32px}.tether-element.tether-theme-arrows.tether-element-attached-right.tether-target-attached-center .tether-content{left:32px}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-left.tether-target-attached-middle .tether-content:before{bottom:100%;left:16px;border-bottom-color:#fff;border-top:0}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-right.tether-target-attached-middle .tether-content:before{bottom:100%;right:16px;border-bottom-color:#fff;border-top:0}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-middle .tether-content:before{top:100%;left:16px;border-top-color:#fff;border-bottom:0}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-middle .tether-content:before{top:100%;right:16px;border-top-color:#fff;border-bottom:0}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-left.tether-target-attached-bottom .tether-content:before{bottom:100%;left:16px;border-bottom-color:#fff;border-top:0}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-right.tether-target-attached-bottom .tether-content:before{bottom:100%;right:16px;border-bottom-color:#fff;border-top:0}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-top .tether-content:before{top:100%;left:16px;border-top-color:#fff;border-bottom:0}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-top .tether-content:before{top:100%;right:16px;border-top-color:#fff;border-bottom:0}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-right.tether-target-attached-left .tether-content:before{top:16px;left:100%;border-left-color:#fff;border-right:0}.tether-element.tether-theme-arrows.tether-element-attached-top.tether-element-attached-left.tether-target-attached-right .tether-content:before{top:16px;right:100%;border-right-color:#fff;border-left:0}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-right.tether-target-attached-left .tether-content:before{bottom:16px;left:100%;border-left-color:#fff;border-right:0}.tether-element.tether-theme-arrows.tether-element-attached-bottom.tether-element-attached-left.tether-target-attached-right .tether-content:before{bottom:16px;right:100%;border-right-color:#fff;border-left:0} -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/tether-theme-basic.css: -------------------------------------------------------------------------------- 1 | .tether-element, .tether-element:after, .tether-element:before, .tether-element *, .tether-element *:after, .tether-element *:before { 2 | box-sizing: border-box; } 3 | 4 | .tether-element { 5 | position: absolute; 6 | display: none; } 7 | .tether-element.tether-open { 8 | display: block; } 9 | 10 | .tether-element.tether-theme-basic { 11 | max-width: 100%; 12 | max-height: 100%; } 13 | .tether-element.tether-theme-basic .tether-content { 14 | border-radius: 5px; 15 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); 16 | font-family: inherit; 17 | background: #fff; 18 | color: inherit; 19 | padding: 1em; 20 | font-size: 1.1em; 21 | line-height: 1.5em; } 22 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/tether-theme-basic.min.css: -------------------------------------------------------------------------------- 1 | .tether-element,.tether-element *,.tether-element :after,.tether-element :before,.tether-element:after,.tether-element:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block}.tether-element.tether-theme-basic{max-width:100%;max-height:100%}.tether-element.tether-theme-basic .tether-content{border-radius:5px;box-shadow:0 2px 8px rgba(0,0,0,.2);font-family:inherit;background:#fff;color:inherit;padding:1em;font-size:1.1em;line-height:1.5em} -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/tether.css: -------------------------------------------------------------------------------- 1 | .tether-element, .tether-element:after, .tether-element:before, .tether-element *, .tether-element *:after, .tether-element *:before { 2 | box-sizing: border-box; } 3 | 4 | .tether-element { 5 | position: absolute; 6 | display: none; } 7 | .tether-element.tether-open { 8 | display: block; } 9 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/tether.min.css: -------------------------------------------------------------------------------- 1 | .tether-element,.tether-element *,.tether-element :after,.tether-element :before,.tether-element:after,.tether-element:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block} -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/thymeleaf-bootstrap-paginator.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Thymeleaf Bootstrap Paginator style. 3 | * 4 | * @since: 1.0.0 5 | * @author Way Lau 6 | */ 7 | 8 | .tbpage-total-elements { 9 | position: relative; 10 | display: block; 11 | padding: .5rem .75rem; 12 | margin-left: -1px; 13 | line-height: 1.25; 14 | color: #464a4c; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/css/toastr.min.css: -------------------------------------------------------------------------------- 1 | .toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#FFF}.toast-message a:hover{color:#CCC;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#FFF;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#FFF;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51A351}.toast-error{background-color:#BD362F}.toast-info{background-color:#2F96B4}.toast-warning{background-color:#F89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}} -------------------------------------------------------------------------------- /blog/src/main/resources/static/data/data1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "name": "Item 0", 5 | "price": "$0" 6 | }, 7 | { 8 | "id": 1, 9 | "name": "Item 1", 10 | "price": "$1" 11 | }, 12 | { 13 | "id": 2, 14 | "name": "Item 2", 15 | "price": "$2" 16 | }, 17 | { 18 | "id": 3, 19 | "name": "Item 3", 20 | "price": "$3" 21 | }, 22 | { 23 | "id": 4, 24 | "name": "Item 4", 25 | "price": "$4" 26 | }, 27 | { 28 | "id": 5, 29 | "name": "Item 5", 30 | "price": "$5" 31 | }, 32 | { 33 | "id": 6, 34 | "name": "Item 6", 35 | "price": "$6" 36 | }, 37 | { 38 | "id": 7, 39 | "name": "Item 7", 40 | "price": "$7" 41 | }, 42 | { 43 | "id": 8, 44 | "name": "Item 8", 45 | "price": "$8" 46 | }, 47 | { 48 | "id": 9, 49 | "name": "Item 9", 50 | "price": "$9" 51 | }, 52 | { 53 | "id": 10, 54 | "name": "Item 10", 55 | "price": "$10" 56 | }, 57 | { 58 | "id": 11, 59 | "name": "Item 11", 60 | "price": "$11" 61 | }, 62 | { 63 | "id": 12, 64 | "name": "Item 12", 65 | "price": "$12" 66 | }, 67 | { 68 | "id": 13, 69 | "name": "Item 13", 70 | "price": "$13" 71 | }, 72 | { 73 | "id": 14, 74 | "name": "Item 14", 75 | "price": "$14" 76 | }, 77 | { 78 | "id": 15, 79 | "name": "Item 15", 80 | "price": "$15" 81 | }, 82 | { 83 | "id": 16, 84 | "name": "Item 16", 85 | "price": "$16" 86 | }, 87 | { 88 | "id": 17, 89 | "name": "Item 17", 90 | "price": "$17" 91 | }, 92 | { 93 | "id": 18, 94 | "name": "Item 18", 95 | "price": "$18" 96 | }, 97 | { 98 | "id": 19, 99 | "name": "Item 19", 100 | "price": "$19" 101 | }, 102 | { 103 | "id": 20, 104 | "name": "Item 20", 105 | "price": "$20" 106 | } 107 | ] -------------------------------------------------------------------------------- /blog/src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/gly-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/gly-halflings-regular.eot -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/gly-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/gly-halflings-regular.ttf -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/gly-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/gly-halflings-regular.woff -------------------------------------------------------------------------------- /blog/src/main/resources/static/fonts/gly-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/fonts/gly-halflings-regular.woff2 -------------------------------------------------------------------------------- /blog/src/main/resources/static/images/avatar-defualt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/images/avatar-defualt.jpg -------------------------------------------------------------------------------- /blog/src/main/resources/static/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/blog/src/main/resources/static/images/delete.png -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/admins/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bolg main JS. 3 | * Created by waylau.com on 2017/3/9. 4 | */ 5 | "use strict"; 6 | //# sourceURL=main.js 7 | 8 | // DOM 加载完再执行 9 | $(function() { 10 | 11 | // 菜单事件 12 | $(".blog-menu .list-group-item").click(function() { 13 | 14 | var url = $(this).attr("url"); 15 | 16 | // 先移除其他的点击样式,再添加当前的点击样式 17 | $(".blog-menu .list-group-item").removeClass("active"); 18 | $(this).addClass("active"); 19 | 20 | // 加载其他模块的页面到右侧工作区 21 | $.ajax({ 22 | url: url, 23 | success: function(data){ 24 | $("#rightContainer").html(data); 25 | }, 26 | error : function() { 27 | alert("error"); 28 | } 29 | }); 30 | }); 31 | 32 | 33 | // 选中菜单第一项 34 | $(".blog-menu .list-group-item:first").trigger("click"); 35 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/catalog-generator.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 页面目录生成器. 3 | * 4 | 比如,我们有如下代码: 5 | 6 |
7 |

OAuth 1.0 的认证流程

8 |

OAuth 2.0 的认证流程

9 |

OAuth 3.0 的认证流程

10 |
11 |
12 | 13 | 要生成目录,按如下方式初始化: 14 | $.catalog("#catalog", ".abc"); 15 | 16 | * @since: 1.0.0 2017-03-26 17 | * @author Way Lau 18 | */ 19 | (function($) { 20 | 21 | "use strict"; 22 | 23 | $.catalog = function(selector, target) { 24 | 25 | $(target + " :header").each(function(i,item){ 26 | var tag = $(item).get(0).localName; 27 | $(item).attr("id","wow"+i); 28 | $(selector).append(''+$(this).text()+'
'); 29 | $(".newh1").css("margin-left",0); 30 | $(".newh2").css("margin-left",5); 31 | $(".newh3").css("margin-left",10); 32 | $(".newh4").css("margin-left",15); 33 | $(".newh5").css("margin-left",20); 34 | $(".newh6").css("margin-left",25); 35 | }); 36 | }; 37 | 38 | })(jQuery); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * index main JS. 3 | * 4 | * @since: 1.0.0 2017/4/12 5 | * @author Way Lau 6 | */ 7 | "use strict"; 8 | //# sourceURL=index.js 9 | 10 | // DOM 加载完再执行 11 | $(function() { 12 | 13 | var _pageSize; // 存储用于搜索 14 | 15 | // 根据用户名、页面索引、页面大小获取用户列表 16 | function getBlogsByName(pageIndex, pageSize) { 17 | $.ajax({ 18 | url: "/blogs", 19 | contentType : 'application/json', 20 | data:{ 21 | "async":true, 22 | "pageIndex":pageIndex, 23 | "pageSize":pageSize, 24 | "keyword":$("#indexkeyword").val() 25 | }, 26 | success: function(data){ 27 | $("#mainContainer").html(data); 28 | 29 | var keyword = $("#indexkeyword").val(); 30 | 31 | // 如果是分类查询,则取消最新、最热选中样式 32 | if (keyword.length > 0) { 33 | $(".nav-item .nav-link").removeClass("active"); 34 | } 35 | }, 36 | error : function() { 37 | toastr.error("error!"); 38 | } 39 | }); 40 | } 41 | 42 | // 分页 43 | $.tbpage("#mainContainer", function (pageIndex, pageSize) { 44 | getBlogsByName(pageIndex, pageSize); 45 | _pageSize = pageSize; 46 | }); 47 | 48 | // 关键字搜索 49 | $("#indexsearch").click(function() { 50 | getBlogsByName(0, _pageSize); 51 | }); 52 | 53 | // 最新\最热切换事件 54 | $(".nav-item .nav-link").click(function() { 55 | 56 | var url = $(this).attr("url"); 57 | 58 | // 先移除其他的点击样式,再添加当前的点击样式 59 | $(".nav-item .nav-link").removeClass("active"); 60 | $(this).addClass("active"); 61 | 62 | // 加载其他模块的页面到右侧工作区 63 | $.ajax({ 64 | url: url+'&async=true', 65 | success: function(data){ 66 | $("#mainContainer").html(data); 67 | }, 68 | error : function() { 69 | toastr.error("error!"); 70 | } 71 | }) 72 | 73 | // 清空搜索框内容 74 | $("#indexkeyword").val(''); 75 | }); 76 | 77 | 78 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/jquery.tagsinput.min.js: -------------------------------------------------------------------------------- 1 | !function(a){var b=new Array,c=new Array;a.fn.doAutosize=function(b){var c=a(this).data("minwidth"),d=a(this).data("maxwidth"),e="",f=a(this),g=a("#"+a(this).data("tester_id"));if(e!==(e=f.val())){var h=e.replace(/&/g,"&").replace(/\s/g," ").replace(//g,">");g.html(h);var i=g.width(),j=i+b.comfortZone>=c?i+b.comfortZone:c,k=f.width(),l=k>j&&j>=c||j>c&&d>j;l&&f.width(j)}},a.fn.resetAutosize=function(b){var c=a(this).data("minwidth")||b.minInputWidth||a(this).width(),d=a(this).data("maxwidth")||b.maxInputWidth||a(this).closest(".tagsinput").width()-b.inputPadding,e=a(this),f=a("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:e.css("fontSize"),fontFamily:e.css("fontFamily"),fontWeight:e.css("fontWeight"),letterSpacing:e.css("letterSpacing"),whiteSpace:"nowrap"}),g=a(this).attr("id")+"_autosize_tester";!a("#"+g).length>0&&(f.attr("id",g),f.appendTo("body")),e.data("minwidth",c),e.data("maxwidth",d),e.data("tester_id",g),e.css("width",c)},a.fn.addTag=function(d,e){return e=jQuery.extend({focus:!1,callback:!0},e),this.each(function(){var f=a(this).attr("id"),g=a(this).val().split(b[f]);if(""==g[0]&&(g=new Array),d=jQuery.trim(d),e.unique){var h=a(this).tagExist(d);1==h&&a("#"+f+"_tag").addClass("not_valid")}else var h=!1;if(""!=d&&1!=h){if(a("").addClass("tag").append(a("").text(d).append("  "),a("",{href:"#",title:"Removing tag",text:"x"}).click(function(){return a("#"+f).removeTag(escape(d))})).insertBefore("#"+f+"_addTag"),g.push(d),a("#"+f+"_tag").val(""),e.focus?a("#"+f+"_tag").focus():a("#"+f+"_tag").blur(),a.fn.tagsInput.updateTagsField(this,g),e.callback&&c[f]&&c[f].onAddTag){var i=c[f].onAddTag;i.call(this,d)}if(c[f]&&c[f].onChange){var j=g.length,i=c[f].onChange;i.call(this,a(this),g[j-1])}}}),!1},a.fn.removeTag=function(d){return d=unescape(d),this.each(function(){var e=a(this).attr("id"),f=a(this).val().split(b[e]);for(a("#"+e+"_tagsinput .tag").remove(),str="",i=0;i=0},a.fn.importTags=function(b){var c=a(this).attr("id");a("#"+c+"_tagsinput .tag").remove(),a.fn.tagsInput.importTags(this,b)},a.fn.tagsInput=function(e){var f=jQuery.extend({interactive:!0,defaultText:"add a tag",minChars:0,width:"300px",height:"100px",autocomplete:{selectFirst:!1},hide:!0,delimiter:",",unique:!0,removeWithBackspace:!0,placeholderColor:"#666666",autosize:!0,comfortZone:20,inputPadding:12},e),g=0;return this.each(function(){if("undefined"==typeof a(this).attr("data-tagsinput-init")){a(this).attr("data-tagsinput-init",!0),f.hide&&a(this).hide();var e=a(this).attr("id");(!e||b[a(this).attr("id")])&&(e=a(this).attr("id","tags"+(new Date).getTime()+g++).attr("id"));var h=jQuery.extend({pid:e,real_input:"#"+e,holder:"#"+e+"_tagsinput",input_wrapper:"#"+e+"_addTag",fake_input:"#"+e+"_tag"},f);b[e]=h.delimiter,(f.onAddTag||f.onRemoveTag||f.onChange)&&(c[e]=new Array,c[e].onAddTag=f.onAddTag,c[e].onRemoveTag=f.onRemoveTag,c[e].onChange=f.onChange);var i='
';if(f.interactive&&(i=i+''),i+='
',a(i).insertAfter(this),a(h.holder).css("width",f.width),a(h.holder).css("min-height",f.height),a(h.holder).css("height",f.height),""!=a(h.real_input).val()&&a.fn.tagsInput.importTags(a(h.real_input),a(h.real_input).val()),f.interactive){if(a(h.fake_input).val(a(h.fake_input).attr("data-default")),a(h.fake_input).css("color",f.placeholderColor),a(h.fake_input).resetAutosize(f),a(h.holder).bind("click",h,function(b){a(b.data.fake_input).focus()}),a(h.fake_input).bind("focus",h,function(b){a(b.data.fake_input).val()==a(b.data.fake_input).attr("data-default")&&a(b.data.fake_input).val(""),a(b.data.fake_input).css("color","#000000")}),void 0!=f.autocomplete_url){autocomplete_options={source:f.autocomplete_url};for(attrname in f.autocomplete)autocomplete_options[attrname]=f.autocomplete[attrname];void 0!==jQuery.Autocompleter?(a(h.fake_input).autocomplete(f.autocomplete_url,f.autocomplete),a(h.fake_input).bind("result",h,function(b,c,d){c&&a("#"+e).addTag(c[0]+"",{focus:!0,unique:f.unique})})):void 0!==jQuery.ui.autocomplete&&(a(h.fake_input).autocomplete(autocomplete_options),a(h.fake_input).bind("autocompleteselect",h,function(b,c){return a(b.data.real_input).addTag(c.item.value,{focus:!0,unique:f.unique}),!1}))}else a(h.fake_input).bind("blur",h,function(b){var c=a(this).attr("data-default");return""!=a(b.data.fake_input).val()&&a(b.data.fake_input).val()!=c?b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length)&&a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:!0,unique:f.unique}):(a(b.data.fake_input).val(a(b.data.fake_input).attr("data-default")),a(b.data.fake_input).css("color",f.placeholderColor)),!1});a(h.fake_input).bind("keypress",h,function(b){return d(b)?(b.preventDefault(),b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length)&&a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:!0,unique:f.unique}),a(b.data.fake_input).resetAutosize(f),!1):void(b.data.autosize&&a(b.data.fake_input).doAutosize(f))}),h.removeWithBackspace&&a(h.fake_input).bind("keydown",function(b){if(8==b.keyCode&&""==a(this).val()){b.preventDefault();var c=a(this).closest(".tagsinput").find(".tag:last").text(),d=a(this).attr("id").replace(/_tag$/,"");c=c.replace(/[\s]+x$/,""),a("#"+d).removeTag(escape(c)),a(this).trigger("focus")}}),a(h.fake_input).blur(),h.unique&&a(h.fake_input).keydown(function(b){(8==b.keyCode||String.fromCharCode(b.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/))&&a(this).removeClass("not_valid")})}}}),this},a.fn.tagsInput.updateTagsField=function(c,d){var e=a(c).attr("id");a(c).val(d.join(b[e]))},a.fn.tagsInput.importTags=function(d,e){a(d).val("");var f=a(d).attr("id"),g=e.split(b[f]);for(i=0;i200 ){ //判断滚动后高度超过200px,就显示 17 | $("#goToTop").fadeIn(400); //淡出 18 | }else{ 19 | $("#goToTop").stop().fadeOut(400); //如果返回或者没有超过,就淡入.必须加上stop()停止之前动画,否则会出现闪动 20 | } 21 | }); 22 | $("#goToTop").click(function(){ //当点击标签的时候,使用animate在200毫秒的时间内,滚到顶部 23 | $("html,body").animate({scrollTop:"0px"},200); 24 | }); 25 | NProgress.done(); 26 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/thymeleaf-bootstrap-paginator.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 分页处理. 3 | * 4 | * @since: 1.0.0 5 | * @author Way Lau 6 | */ 7 | (function($) { 8 | 9 | "use strict"; 10 | 11 | /** 12 | * handler:pageIndex 所选页面的索引,从0开始;pageSize 页面的大小,这里默认是10。 13 | */ 14 | $.tbpage = function(selector, handler) { 15 | 16 | $(selector).off("click", ".tbpage-item").on("click", ".tbpage-item", function() { 17 | 18 | var pageIndex = $(this).attr("pageIndex"); 19 | 20 | var pageSize = $('.tbpage-size option:selected').val(); 21 | // 判断所选元素是否为当前页面 22 | // 若不是当前页面才需要处理 23 | if($(this).parent().attr("class").indexOf("active")>0){ 24 | //console.log("为当前页面"); 25 | }else{ 26 | handler(pageIndex, pageSize); 27 | } 28 | 29 | }); 30 | 31 | 32 | $(selector).off("change", ".tbpage-size").on("change", ".tbpage-size", function() { 33 | 34 | var pageIndex = $(this).attr("pageIndex"); 35 | 36 | var pageSize = $('.tbpage-size option:selected').val(); 37 | 38 | handler(pageIndex, pageSize); 39 | }); 40 | }; 41 | 42 | })(jQuery); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/toastr.min.js: -------------------------------------------------------------------------------- 1 | !function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return g({type:O.error,iconClass:m().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=m()),v=e("#"+t.containerId),v.length?v:(n&&(v=d(t)),v)}function o(e,t,n){return g({type:O.info,iconClass:m().iconClasses.info,message:e,optionsOverride:n,title:t})}function s(e){C=e}function i(e,t,n){return g({type:O.success,iconClass:m().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return g({type:O.warning,iconClass:m().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e,t){var o=m();v||n(o),u(e,o,t)||l(o)}function c(t){var o=m();return v||n(o),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function l(t){for(var n=v.children(),o=n.length-1;o>=0;o--)u(e(n[o]),t)}function u(t,n,o){var s=!(!o||!o.force)&&o.force;return!(!t||!s&&0!==e(":focus",t).length)&&(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0)}function d(t){return v=e("
").attr("id",t.containerId).addClass(t.positionClass),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1}}function f(e){C&&C(e)}function g(t){function o(e){return null==e&&(e=""),e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function s(){c(),u(),d(),p(),g(),C(),l(),i()}function i(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}I.attr("aria-live",e)}function a(){E.closeOnHover&&I.hover(H,D),!E.onclick&&E.tapToDismiss&&I.click(b),E.closeButton&&j&&j.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),E.onCloseClick&&E.onCloseClick(e),b(!0)}),E.onclick&&I.click(function(e){E.onclick(e),b()})}function r(){I.hide(),I[E.showMethod]({duration:E.showDuration,easing:E.showEasing,complete:E.onShown}),E.timeOut>0&&(k=setTimeout(b,E.timeOut),F.maxHideTime=parseFloat(E.timeOut),F.hideEta=(new Date).getTime()+F.maxHideTime,E.progressBar&&(F.intervalId=setInterval(x,10)))}function c(){t.iconClass&&I.addClass(E.toastClass).addClass(y)}function l(){E.newestOnTop?v.prepend(I):v.append(I)}function u(){if(t.title){var e=t.title;E.escapeHtml&&(e=o(t.title)),M.append(e).addClass(E.titleClass),I.append(M)}}function d(){if(t.message){var e=t.message;E.escapeHtml&&(e=o(t.message)),B.append(e).addClass(E.messageClass),I.append(B)}}function p(){E.closeButton&&(j.addClass(E.closeClass).attr("role","button"),I.prepend(j))}function g(){E.progressBar&&(q.addClass(E.progressClass),I.prepend(q))}function C(){E.rtl&&I.addClass("rtl")}function O(e,t){if(e.preventDuplicates){if(t.message===w)return!0;w=t.message}return!1}function b(t){var n=t&&E.closeMethod!==!1?E.closeMethod:E.hideMethod,o=t&&E.closeDuration!==!1?E.closeDuration:E.hideDuration,s=t&&E.closeEasing!==!1?E.closeEasing:E.hideEasing;if(!e(":focus",I).length||t)return clearTimeout(F.intervalId),I[n]({duration:o,easing:s,complete:function(){h(I),clearTimeout(k),E.onHidden&&"hidden"!==P.state&&E.onHidden(),P.state="hidden",P.endTime=new Date,f(P)}})}function D(){(E.timeOut>0||E.extendedTimeOut>0)&&(k=setTimeout(b,E.extendedTimeOut),F.maxHideTime=parseFloat(E.extendedTimeOut),F.hideEta=(new Date).getTime()+F.maxHideTime)}function H(){clearTimeout(k),F.hideEta=0,I.stop(!0,!0)[E.showMethod]({duration:E.showDuration,easing:E.showEasing})}function x(){var e=(F.hideEta-(new Date).getTime())/F.maxHideTime*100;q.width(e+"%")}var E=m(),y=t.iconClass||E.iconClass;if("undefined"!=typeof t.optionsOverride&&(E=e.extend(E,t.optionsOverride),y=t.optionsOverride.iconClass||y),!O(E,t)){T++,v=n(E,!0);var k=null,I=e("
"),M=e("
"),B=e("
"),q=e("
"),j=e(E.closeHtml),F={intervalId:null,hideEta:null,maxHideTime:null},P={toastId:T,state:"visible",startTime:new Date,options:E,map:t};return s(),r(),a(),f(P),E.debug&&console&&console.log(P),I}}function m(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),w=void 0))}var v,C,w,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:c,error:t,getContainer:n,info:o,options:{},subscribe:s,success:i,version:"2.1.3",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)}); 2 | //# sourceMappingURL=toastr.js.map 3 | -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/users/main.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bolg main JS. 3 | * 4 | * @since: 1.0.0 2017/3/9 5 | * @author Way Lau 6 | */ 7 | "use strict"; 8 | //# sourceURL=main.js 9 | 10 | // DOM 加载完再执行 11 | $(function() { 12 | 13 | var _pageSize; // 存储用于搜索 14 | 15 | // 根据用户名、页面索引、页面大小获取用户列表 16 | function getUersByName(pageIndex, pageSize) { 17 | $.ajax({ 18 | url: "/users", 19 | contentType : 'application/json', 20 | data:{ 21 | "async":true, 22 | "pageIndex":pageIndex, 23 | "pageSize":pageSize, 24 | "name":$("#searchName").val() 25 | }, 26 | success: function(data){ 27 | $("#mainContainer").html(data); 28 | }, 29 | error : function() { 30 | toastr.error("error!"); 31 | } 32 | }); 33 | } 34 | 35 | // 分页 36 | $.tbpage("#mainContainer", function (pageIndex, pageSize) { 37 | getUersByName(pageIndex, pageSize); 38 | _pageSize = pageSize; 39 | }); 40 | 41 | // 搜索 42 | $("#searchNameBtn").click(function() { 43 | getUersByName(0, _pageSize); 44 | }); 45 | 46 | // 获取添加用户的界面 47 | $("#addUser").click(function() { 48 | $.ajax({ 49 | url: "/users/add", 50 | success: function(data){ 51 | $("#userFormContainer").html(data); 52 | }, 53 | error : function(data) { 54 | toastr.error("error!"); 55 | } 56 | }); 57 | }); 58 | 59 | // 获取编辑用户的界面 60 | $("#rightContainer").on("click",".blog-edit-user", function () { 61 | $.ajax({ 62 | url: "/users/edit/" + $(this).attr("userId"), 63 | success: function(data){ 64 | $("#userFormContainer").html(data); 65 | }, 66 | error : function() { 67 | toastr.error("error!"); 68 | } 69 | }); 70 | }); 71 | 72 | // 提交变更后,清空表单 73 | $("#submitEdit").click(function() { 74 | $.ajax({ 75 | url: "/users", 76 | type: 'POST', 77 | data:$('#userForm').serialize(), 78 | success: function(data){ 79 | $('#userForm')[0].reset(); 80 | 81 | if (data.success) { 82 | // 从新刷新主界面 83 | getUersByName(0, _pageSize); 84 | } else { 85 | toastr.error(data.message); 86 | } 87 | 88 | }, 89 | error : function() { 90 | toastr.error("error!"); 91 | } 92 | }); 93 | }); 94 | 95 | // 删除用户 96 | $("#rightContainer").on("click",".blog-delete-user", function () { 97 | // 获取 CSRF Token 98 | var csrfToken = $("meta[name='_csrf']").attr("content"); 99 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 100 | 101 | 102 | $.ajax({ 103 | url: "/users/" + $(this).attr("userId") , 104 | type: 'DELETE', 105 | beforeSend: function(request) { 106 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 107 | }, 108 | success: function(data){ 109 | if (data.success) { 110 | // 从新刷新主界面 111 | getUersByName(0, _pageSize); 112 | } else { 113 | toastr.error(data.message); 114 | } 115 | }, 116 | error : function() { 117 | toastr.error("error!"); 118 | } 119 | }); 120 | }); 121 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/userspace/blog.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * blog.html 页面脚本. 3 | * 4 | * @since: 1.0.0 2017-03-26 5 | * @author Way Lau 6 | */ 7 | "use strict"; 8 | //# sourceURL=blog.js 9 | 10 | // DOM 加载完再执行 11 | $(function() { 12 | $.catalog("#catalog", ".post-content"); 13 | 14 | // 处理删除博客事件 15 | 16 | $(".blog-content-container").on("click",".blog-delete-blog", function () { 17 | // 获取 CSRF Token 18 | var csrfToken = $("meta[name='_csrf']").attr("content"); 19 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 20 | 21 | 22 | $.ajax({ 23 | url: blogUrl, 24 | type: 'DELETE', 25 | beforeSend: function(request) { 26 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 27 | }, 28 | success: function(data){ 29 | if (data.success) { 30 | // 成功后,重定向 31 | window.location = data.body; 32 | } else { 33 | toastr.error(data.message); 34 | } 35 | }, 36 | error : function() { 37 | toastr.error("error!"); 38 | } 39 | }); 40 | }); 41 | 42 | // 获取评论列表 43 | function getCommnet(blogId) { 44 | // 获取 CSRF Token 45 | var csrfToken = $("meta[name='_csrf']").attr("content"); 46 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 47 | 48 | $.ajax({ 49 | url: '/comments', 50 | type: 'GET', 51 | data:{"blogId":blogId}, 52 | beforeSend: function(request) { 53 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 54 | }, 55 | success: function(data){ 56 | $("#mainContainer").html(data); 57 | 58 | }, 59 | error : function() { 60 | toastr.error("error!"); 61 | } 62 | }); 63 | } 64 | 65 | // 提交评论 66 | $(".blog-content-container").on("click","#submitComment", function () { 67 | // 获取 CSRF Token 68 | var csrfToken = $("meta[name='_csrf']").attr("content"); 69 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 70 | 71 | $.ajax({ 72 | url: '/comments', 73 | type: 'POST', 74 | data:{"blogId":blogId, "commentContent":$('#commentContent').val()}, 75 | beforeSend: function(request) { 76 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 77 | }, 78 | success: function(data){ 79 | if (data.success) { 80 | // 清空评论框 81 | $('#commentContent').val(''); 82 | // 获取评论列表 83 | getCommnet(blogId); 84 | } else { 85 | toastr.error(data.message); 86 | } 87 | }, 88 | error : function() { 89 | toastr.error("error!"); 90 | } 91 | }); 92 | }); 93 | 94 | // 删除评论 95 | $(".blog-content-container").on("click",".blog-delete-comment", function () { 96 | // 获取 CSRF Token 97 | var csrfToken = $("meta[name='_csrf']").attr("content"); 98 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 99 | 100 | $.ajax({ 101 | url: '/comments/'+$(this).attr("commentId")+'?blogId='+blogId, 102 | type: 'DELETE', 103 | beforeSend: function(request) { 104 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 105 | }, 106 | success: function(data){ 107 | if (data.success) { 108 | // 获取评论列表 109 | getCommnet(blogId); 110 | } else { 111 | toastr.error(data.message); 112 | } 113 | }, 114 | error : function() { 115 | toastr.error("error!"); 116 | } 117 | }); 118 | }); 119 | 120 | 121 | // 提交点赞 122 | $(".blog-content-container").on("click","#submitVote", function () { 123 | // 获取 CSRF Token 124 | var csrfToken = $("meta[name='_csrf']").attr("content"); 125 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 126 | 127 | $.ajax({ 128 | url: '/votes', 129 | type: 'POST', 130 | data:{"blogId":blogId}, 131 | beforeSend: function(request) { 132 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 133 | }, 134 | success: function(data){ 135 | if (data.success) { 136 | toastr.info(data.message); 137 | // 成功后,重定向 138 | window.location = blogUrl; 139 | } else { 140 | toastr.error(data.message); 141 | } 142 | }, 143 | error : function() { 144 | toastr.error("error!"); 145 | } 146 | }); 147 | }); 148 | 149 | // 提交点赞 150 | $(".blog-content-container").on("click","#cancelVote", function () { 151 | // 获取 CSRF Token 152 | var csrfToken = $("meta[name='_csrf']").attr("content"); 153 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 154 | 155 | $.ajax({ 156 | url: '/votes/'+$(this).attr('voteId')+'?blogId='+blogId, 157 | type: 'DELETE', 158 | beforeSend: function(request) { 159 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 160 | }, 161 | success: function(data){ 162 | if (data.success) { 163 | toastr.info(data.message); 164 | // 成功后,重定向 165 | window.location = blogUrl; 166 | } else { 167 | toastr.error(data.message); 168 | } 169 | }, 170 | error : function() { 171 | toastr.error("error!"); 172 | } 173 | }); 174 | }); 175 | 176 | // 初始化 博客评论 177 | getCommnet(blogId); 178 | 179 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/userspace/blogedit.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * blogedit.html 页面脚本. 3 | * 4 | * @since: 1.0.0 2017-03-26 5 | * @author Way Lau 6 | */ 7 | "use strict"; 8 | //# sourceURL=blogedit.js 9 | 10 | // DOM 加载完再执行 11 | $(function() { 12 | 13 | // 初始化 md 编辑器 14 | $("#md").markdown({ 15 | language: 'zh', 16 | fullscreen: { 17 | enable: true 18 | }, 19 | resize:'vertical', 20 | localStorage:'md', 21 | imgurl: 'http://localhost:8081', 22 | base64url: 'http://localhost:8081' 23 | }); 24 | 25 | 26 | 27 | // 初始化下拉 28 | $('.form-control-chosen').chosen(); 29 | 30 | 31 | // 初始化标签 32 | $('.form-control-tag').tagsInput({ 33 | 'defaultText':'输入标签' 34 | 35 | }); 36 | 37 | $("#uploadImage").click(function() { 38 | $.ajax({ 39 | url: 'http://localhost:8081/upload', 40 | type: 'POST', 41 | cache: false, 42 | data: new FormData($('#uploadformid')[0]), 43 | processData: false, 44 | contentType: false, 45 | success: function(data){ 46 | var mdcontent=$("#md").val(); 47 | $("#md").val(mdcontent + "\n![]("+data +") \n"); 48 | 49 | } 50 | }).done(function(res) { 51 | $('#file').val(''); 52 | }).fail(function(res) {}); 53 | }) 54 | 55 | // 发布博客 56 | $("#submitBlog").click(function() { 57 | 58 | // 获取 CSRF Token 59 | var csrfToken = $("meta[name='_csrf']").attr("content"); 60 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 61 | 62 | $.ajax({ 63 | url: '/u/'+ $(this).attr("userName") + '/blogs/edit', 64 | type: 'POST', 65 | contentType: "application/json; charset=utf-8", 66 | data:JSON.stringify({"id":$('#blogId').val(), 67 | "title": $('#title').val(), 68 | "summary": $('#summary').val() , 69 | "content": $('#md').val(), 70 | "catalog":{"id":$('#catalogSelect').val()}, 71 | "tags":$('.form-control-tag').val() 72 | }), 73 | beforeSend: function(request) { 74 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 75 | }, 76 | success: function(data){ 77 | if (data.success) { 78 | // 成功后,重定向 79 | window.location = data.body; 80 | } else { 81 | toastr.error("error!"+data.message); 82 | } 83 | 84 | }, 85 | error : function() { 86 | toastr.error("error!"); 87 | } 88 | }) 89 | }) 90 | 91 | 92 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/userspace/main.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Avatar JS. 3 | * 4 | * @since: 1.0.0 2017/4/6 5 | * @author Way Lau 6 | */ 7 | "use strict"; 8 | //# sourceURL=main.js 9 | 10 | // DOM 加载完再执行 11 | $(function() { 12 | var avatarApi; 13 | 14 | // 获取编辑用户头像的界面 15 | $(".blog-content-container").on("click",".blog-edit-avatar", function () { 16 | avatarApi = "/u/"+$(this).attr("userName")+"/avatar"; 17 | $.ajax({ 18 | url: avatarApi, 19 | success: function(data){ 20 | $("#avatarFormContainer").html(data); 21 | }, 22 | error : function() { 23 | toastr.error("error!"); 24 | } 25 | }); 26 | }); 27 | 28 | /** 29 | * 将以base64的图片url数据转换为Blob 30 | * @param urlData 31 | * 用url方式表示的base64图片数据 32 | */ 33 | function convertBase64UrlToBlob(urlData){ 34 | 35 | var bytes=window.atob(urlData.split(',')[1]); //去掉url的头,并转换为byte 36 | 37 | //处理异常,将ascii码小于0的转换为大于0 38 | var ab = new ArrayBuffer(bytes.length); 39 | var ia = new Uint8Array(ab); 40 | for (var i = 0; i < bytes.length; i++) { 41 | ia[i] = bytes.charCodeAt(i); 42 | } 43 | 44 | return new Blob( [ab] , {type : 'image/png'}); 45 | } 46 | 47 | // 提交用户头像的图片数据 48 | $("#submitEditAvatar").on("click", function () { 49 | var form = $('#avatarformid')[0]; 50 | var formData = new FormData(form); //这里连带form里的其他参数也一起提交了,如果不需要提交其他参数可以直接FormData无参数的构造函数 51 | var base64Codes = $(".cropImg > img").attr("src"); 52 | formData.append("file",convertBase64UrlToBlob(base64Codes)); //append函数的第一个参数是后台获取数据的参数名,和html标签的input的name属性功能相同 53 | 54 | $.ajax({ 55 | url: 'http://localhost:8081/upload', 56 | type: 'POST', 57 | cache: false, 58 | data: formData, 59 | processData: false, 60 | contentType: false, 61 | success: function(data){ 62 | 63 | var avatarUrl = data; 64 | 65 | // 获取 CSRF Token 66 | var csrfToken = $("meta[name='_csrf']").attr("content"); 67 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 68 | // 保存头像更改到数据库 69 | $.ajax({ 70 | url: avatarApi, 71 | type: 'POST', 72 | contentType: "application/json; charset=utf-8", 73 | data: JSON.stringify({"id":Number($("#userId").val()), "avatar":avatarUrl}), 74 | beforeSend: function(request) { 75 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 76 | }, 77 | success: function(data){ 78 | if (data.success) { 79 | // 成功后,置换头像图片 80 | $(".blog-avatar").attr("src", data.avatarUrl); 81 | } else { 82 | toastr.error("error!"+data.message); 83 | } 84 | 85 | }, 86 | error : function() { 87 | toastr.error("error!"); 88 | } 89 | }); 90 | }, 91 | error : function() { 92 | toastr.error("error!"); 93 | } 94 | }) 95 | }); 96 | 97 | 98 | 99 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/static/js/userspace/u.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * u main JS. 3 | * 4 | * @since: 1.0.0 2017/3/9 5 | * @author Way Lau 6 | */ 7 | "use strict"; 8 | //# sourceURL=u.js 9 | 10 | // DOM 加载完再执行 11 | $(function() { 12 | 13 | var _pageSize; // 存储用于搜索 14 | 15 | // 根据用户名、页面索引、页面大小获取用户列表 16 | function getBlogsByName(pageIndex, pageSize) { 17 | $.ajax({ 18 | url: "/u/"+ username +"/blogs", 19 | contentType : 'application/json', 20 | data:{ 21 | "async":true, 22 | "pageIndex":pageIndex, 23 | "pageSize":pageSize, 24 | "catalog":catalogId, 25 | "keyword":$("#keyword").val() 26 | }, 27 | success: function(data){ 28 | $("#mainContainer").html(data); 29 | 30 | // 如果是分类查询,则取消最新、最热选中样式 31 | if (catalogId) { 32 | $(".nav-item .nav-link").removeClass("active"); 33 | } 34 | }, 35 | error : function() { 36 | toastr.error("error!"); 37 | } 38 | }); 39 | } 40 | 41 | // 分页 42 | $.tbpage("#mainContainer", function (pageIndex, pageSize) { 43 | getBlogsByName(pageIndex, pageSize); 44 | _pageSize = pageSize; 45 | }); 46 | 47 | // 关键字搜索 48 | $("#searchBlogs").click(function() { 49 | getBlogsByName(0, _pageSize); 50 | }); 51 | 52 | // 最新\最热切换事件 53 | $(".nav-item .nav-link").click(function() { 54 | 55 | var url = $(this).attr("url"); 56 | 57 | // 先移除其他的点击样式,再添加当前的点击样式 58 | $(".nav-item .nav-link").removeClass("active"); 59 | $(this).addClass("active"); 60 | 61 | // 加载其他模块的页面到右侧工作区 62 | $.ajax({ 63 | url: url+'&async=true', 64 | success: function(data){ 65 | $("#mainContainer").html(data); 66 | }, 67 | error : function() { 68 | toastr.error("error!"); 69 | } 70 | }) 71 | 72 | // 清空搜索框内容 73 | $("#keyword").val(''); 74 | }); 75 | 76 | 77 | // 获取分类列表 78 | function getCatalogs(username) { 79 | // 获取 CSRF Token 80 | 81 | $.ajax({ 82 | url: '/catalogs', 83 | type: 'GET', 84 | data:{"username":username}, 85 | success: function(data){ 86 | $("#catalogMain").html(data); 87 | }, 88 | error : function() { 89 | toastr.error("error!"); 90 | } 91 | }); 92 | } 93 | 94 | 95 | // 获取编辑分类的页面 96 | $(".blog-content-container").on("click",".blog-add-catalog", function () { 97 | $.ajax({ 98 | url: '/catalogs/edit', 99 | type: 'GET', 100 | success: function(data){ 101 | $("#catalogFormContainer").html(data); 102 | }, 103 | error : function() { 104 | toastr.error("error!"); 105 | } 106 | }); 107 | }); 108 | 109 | // 获取编辑某个分类的页面 110 | $(".blog-content-container").on("click",".blog-edit-catalog", function () { 111 | 112 | $.ajax({ 113 | url: '/catalogs/edit/'+$(this).attr('catalogId'), 114 | type: 'GET', 115 | success: function(data){ 116 | $("#catalogFormContainer").html(data); 117 | }, 118 | error : function() { 119 | toastr.error("error!"); 120 | } 121 | }); 122 | }); 123 | 124 | // 提交分类 125 | $("#submitEditCatalog").click(function() { 126 | // 获取 CSRF Token 127 | var csrfToken = $("meta[name='_csrf']").attr("content"); 128 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 129 | 130 | $.ajax({ 131 | url: '/catalogs', 132 | type: 'POST', 133 | contentType: "application/json; charset=utf-8", 134 | data:JSON.stringify({"username":username, "catalog":{"id":$('#catalogId').val(), "name":$('#catalogName').val()}}), 135 | beforeSend: function(request) { 136 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 137 | }, 138 | success: function(data){ 139 | if (data.success) { 140 | toastr.info(data.message); 141 | // 成功后,刷新列表 142 | getCatalogs(username); 143 | } else { 144 | toastr.error(data.message); 145 | } 146 | }, 147 | error : function() { 148 | toastr.error("error!"); 149 | } 150 | }); 151 | }); 152 | 153 | // 删除分类 154 | $(".blog-content-container").on("click",".blog-delete-catalog", function () { 155 | // 获取 CSRF Token 156 | var csrfToken = $("meta[name='_csrf']").attr("content"); 157 | var csrfHeader = $("meta[name='_csrf_header']").attr("content"); 158 | 159 | $.ajax({ 160 | url: '/catalogs/'+$(this).attr('catalogid')+'?username='+username, 161 | type: 'DELETE', 162 | beforeSend: function(request) { 163 | request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token 164 | }, 165 | success: function(data){ 166 | if (data.success) { 167 | toastr.info(data.message); 168 | // 成功后,刷新列表 169 | getCatalogs(username); 170 | } else { 171 | toastr.error(data.message); 172 | } 173 | }, 174 | error : function() { 175 | toastr.error("error!"); 176 | } 177 | }); 178 | }); 179 | 180 | // 根据分类查询 181 | $(".blog-content-container").on("click",".blog-query-by-catalog", function () { 182 | catalogId = $(this).attr('catalogId'); 183 | getBlogsByName(0, _pageSize); 184 | }); 185 | 186 | 187 | 188 | 189 | getCatalogs(username); 190 | 191 | }); -------------------------------------------------------------------------------- /blog/src/main/resources/templates/admins/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 43 | 44 | 45 | 46 | 47 |
...
48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/fragments/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/fragments/header.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 103 | 104 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/fragments/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 |
20 |

21 | 22 | 23 | 24 | 25 | 26 | 28 | OAuth 2.0 认证的原理与实践 29 | 30 |

31 |

使用 OAuth 2.0 认证的的好处是显然易见的。你只需要用同一个账号密码,就能在各个网站进行访问,而免去了在每个网站都进行注册的繁琐过程。 本文将介绍 32 | OAuth 2.0 的原理,并基于 Spring Security 和 GitHub 账号,来演示 OAuth 2.0 的认证的过程。

33 |
34 | waylau 发表于 [[${#dates.format(blog.createTime, 'yyyy-MM-dd HH:mm')}]] 35 | 37 |
38 |
39 |
40 | 41 |
...
42 |
43 |
44 | 45 |
46 | 47 | 48 |
49 |
50 | 51 |
52 |
热门标签
53 |
54 |
55 |
56 | Web Design 58 |
59 |
60 |
61 |
62 | 63 | 64 |
65 |
热门用户
66 |
67 |
68 |
69 | 70 | 71 | 72 | 73 | 74 |
75 |
76 |
77 |
78 | 79 | 80 |
81 |
热门文章
82 | 83 | 88 | 89 |
90 | 91 | 92 |
93 |
最新发布
94 |
95 | 100 |
101 | 102 |
103 |
104 |
105 | 106 |
107 | 108 | 109 |
110 | 111 | 112 | 113 |
...
114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 |

请登录

12 | 13 |
14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 记住我 24 |
25 |
26 | 27 |
28 |
29 |

30 |
31 |
32 |
33 | 34 | 35 |
...
36 | 37 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/register.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 | 36 | 37 |
...
38 | 39 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/users/add.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 29 |
30 |
31 | 32 |
33 | 34 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/users/edit.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 29 |
30 |
31 | 32 |
33 | 34 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/users/list.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 7 | 10 | 13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 51 | 52 | 53 | 54 | 55 |
ID账号姓名邮箱角色操作
11waylauwaylauwaylau 40 |
41 | 45 | 48 | 49 |
50 |
56 |
...
57 | 58 |
59 |
60 | 61 | 62 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/userspace/avatar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26 | 27 | 28 |
29 | 图片剪切 30 | 31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 |
Loading...
39 |
40 |
41 | 剪切 42 |
43 |
44 | 放大 45 | 缩小 46 |
47 | 48 |
49 |
50 |
51 |
52 | 53 | 71 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/userspace/blogedit.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 18 | 19 |
20 | 21 |
22 | 23 | 24 |
25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 | 37 |
38 |
39 | 40 |
41 | 42 |
43 | 44 | 45 |
46 | 47 | 48 |
49 |
图片上传
50 |
51 |
52 |
53 | 54 | 55 |
56 | 57 | 58 | 59 |
60 | 61 |
62 |
63 |
64 | 65 |
66 | 67 |
68 |
博客设置
69 |
70 |
71 |
72 | 标签: 73 |
74 |
75 |
76 |
77 | 分类: 80 |
81 |
82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 | 90 |
91 |
92 | 93 |
94 | 95 | 96 |
97 | 98 | 99 | 100 |
...
101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/userspace/catalogedit.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 | 7 | 8 |
9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /blog/src/main/resources/templates/userspace/profile.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 |
15 | 16 |
17 |
18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 | 44 | 45 |
46 |
个人设置
47 |
48 |
49 | 50 | 51 |
52 | 53 | 54 | 55 |
56 |
57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |
69 | 70 |
71 |
72 | 73 |
74 |
75 | 76 | 77 |
78 |
79 | 80 | 81 |
82 | 83 | 84 | 85 | 105 | 106 |
...
107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /blog/src/test/java/com/pjb/blog/BlogApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.pjb.blog; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class BlogApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /fileserver/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /fileserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.1.RELEASE 9 | 10 | 11 | com.pjb 12 | fileserver 13 | 0.0.1-SNAPSHOT 14 | fileserver 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-thymeleaf 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-data-mongodb 33 | 34 | 35 | org.projectlombok 36 | lombok 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-test 41 | test 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /fileserver/src/main/java/com/pjb/fileserver/FileserverApplication.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class FileserverApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(FileserverApplication.class, args); 11 | } 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /fileserver/src/main/java/com/pjb/fileserver/controller/FileController.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver.controller; 2 | 3 | import java.io.IOException; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | import com.pjb.fileserver.domain.File; 7 | import com.pjb.fileserver.service.FileService; 8 | import com.pjb.fileserver.util.MD5Util; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.HttpHeaders; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.web.bind.annotation.CrossOrigin; 17 | import org.springframework.web.bind.annotation.GetMapping; 18 | import org.springframework.web.bind.annotation.PathVariable; 19 | import org.springframework.web.bind.annotation.PostMapping; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RequestParam; 22 | import org.springframework.web.bind.annotation.ResponseBody; 23 | import org.springframework.web.multipart.MultipartFile; 24 | import org.springframework.web.servlet.mvc.support.RedirectAttributes; 25 | 26 | 27 | @CrossOrigin(origins = "*", maxAge = 3600) // 允许所有域名访问 28 | @Slf4j 29 | @Controller 30 | public class FileController { 31 | 32 | private final FileService fileService; 33 | 34 | @Autowired 35 | public FileController(FileService fileService) { 36 | this.fileService = fileService; 37 | } 38 | 39 | @RequestMapping(value = "/") 40 | public String index(Model model) { 41 | model.addAttribute("files", fileService.listFiles()); 42 | return "index"; 43 | } 44 | 45 | /** 46 | * 获取文件片信息 47 | */ 48 | @GetMapping("/{id}") 49 | @ResponseBody 50 | public ResponseEntity serveFile(@PathVariable String id) { 51 | 52 | File file = fileService.getFileById(id); 53 | 54 | if (file != null) { 55 | return ResponseEntity 56 | .ok() 57 | .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=\"" + file.getName() + "\"") 58 | .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream" ) 59 | .header(HttpHeaders.CONTENT_LENGTH, file.getSize()+"") 60 | .header("Connection", "close") 61 | .body( file.getContent()); 62 | } else { 63 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not fount"); 64 | } 65 | 66 | } 67 | 68 | /** 69 | * 在线显示文件 70 | */ 71 | @GetMapping("/view/{id}") 72 | @ResponseBody 73 | public ResponseEntity serveFileOnline(@PathVariable String id) { 74 | 75 | File file = fileService.getFileById(id); 76 | 77 | if (file != null) { 78 | return ResponseEntity 79 | .ok() 80 | .header(HttpHeaders.CONTENT_DISPOSITION, "fileName=\"" + file.getName() + "\"") 81 | .header(HttpHeaders.CONTENT_TYPE, file.getContentType() ) 82 | .header(HttpHeaders.CONTENT_LENGTH, file.getSize()+"") 83 | .header("Connection", "close") 84 | .body( file.getContent()); 85 | } else { 86 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not fount"); 87 | } 88 | 89 | } 90 | 91 | /** 92 | * 上传 93 | */ 94 | @PostMapping("/") 95 | public String handleFileUpload(@RequestParam("file") MultipartFile file, 96 | RedirectAttributes redirectAttributes) { 97 | 98 | try { 99 | File f = new File(file.getOriginalFilename(), file.getContentType(), file.getSize(), file.getBytes()); 100 | f.setMd5( MD5Util.getMD5(file.getInputStream()) ); 101 | fileService.saveFile(f); 102 | } catch (IOException | NoSuchAlgorithmException ex) { 103 | log.error("error",ex); 104 | redirectAttributes.addFlashAttribute("message", 105 | "Your " + file.getOriginalFilename() + " is wrong!"); 106 | return "redirect:/"; 107 | } 108 | 109 | redirectAttributes.addFlashAttribute("message", 110 | "You successfully uploaded " + file.getOriginalFilename() + "!"); 111 | 112 | return "redirect:/"; 113 | } 114 | 115 | @PostMapping("/upload") 116 | @ResponseBody 117 | public ResponseEntity handleFileUpload(@RequestParam("file") MultipartFile file) { 118 | File returnFile; 119 | try { 120 | File f = new File(file.getOriginalFilename(), file.getContentType(), file.getSize(),file.getBytes()); 121 | f.setMd5( MD5Util.getMD5(file.getInputStream()) ); 122 | returnFile = fileService.saveFile(f); 123 | returnFile.setPath("http://localhost:8081/view/"+f.getId()); 124 | returnFile.setContent(null) ; 125 | return ResponseEntity.status(HttpStatus.OK).body("http://localhost:8081/view/"+f.getId()); 126 | 127 | } catch (IOException | NoSuchAlgorithmException ex) { 128 | log.error("error",ex); 129 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); 130 | } 131 | 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /fileserver/src/main/java/com/pjb/fileserver/domain/File.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver.domain; 2 | 3 | import java.util.Date; 4 | 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.mongodb.core.mapping.Document; 10 | 11 | @Document 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class File { 16 | @Id // 主键 17 | private String id; 18 | private String name; 19 | private String contentType; 20 | private long size; 21 | private Date uploadDate; 22 | private String md5; 23 | private byte[] content; 24 | private String path; 25 | 26 | public File(String name, String contentType, long size,byte[] content) { 27 | this.name = name; 28 | this.contentType = contentType; 29 | this.size = size; 30 | this.uploadDate = new Date(); 31 | this.content = content; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object object) { 36 | if (this == object) { 37 | return true; 38 | } 39 | if (object == null || getClass() != object.getClass()) { 40 | return false; 41 | } 42 | File fileInfo = (File) object; 43 | return java.util.Objects.equals(size, fileInfo.size) 44 | && java.util.Objects.equals(name, fileInfo.name) 45 | && java.util.Objects.equals(contentType, fileInfo.contentType) 46 | && java.util.Objects.equals(uploadDate, fileInfo.uploadDate) 47 | && java.util.Objects.equals(md5, fileInfo.md5) 48 | && java.util.Objects.equals(id, fileInfo.id); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return java.util.Objects.hash(name, contentType, size, uploadDate, md5, id); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /fileserver/src/main/java/com/pjb/fileserver/repository/FileRepository.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver.repository; 2 | 3 | import com.pjb.fileserver.domain.File; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | 6 | /** 7 | * File 存储库. 8 | */ 9 | public interface FileRepository extends MongoRepository { 10 | } 11 | -------------------------------------------------------------------------------- /fileserver/src/main/java/com/pjb/fileserver/service/FileService.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver.service; 2 | 3 | 4 | import com.pjb.fileserver.domain.File; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * File 服务接口. 10 | */ 11 | public interface FileService { 12 | /** 13 | * 保存文件 14 | */ 15 | File saveFile(File file); 16 | 17 | /** 18 | * 删除文件 19 | */ 20 | void removeFile(String id); 21 | 22 | /** 23 | * 根据id获取文件 24 | */ 25 | File getFileById(String id); 26 | 27 | /** 28 | * 获取文件列表 29 | */ 30 | List listFiles(); 31 | } 32 | -------------------------------------------------------------------------------- /fileserver/src/main/java/com/pjb/fileserver/service/FileServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver.service; 2 | 3 | import java.util.List; 4 | 5 | import com.pjb.fileserver.domain.File; 6 | import com.pjb.fileserver.repository.FileRepository; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | 11 | 12 | /** 13 | * File 服务. 14 | */ 15 | @Service 16 | public class FileServiceImpl implements FileService { 17 | 18 | private final FileRepository fileRepository; 19 | 20 | @Autowired 21 | public FileServiceImpl(FileRepository fileRepository) { 22 | this.fileRepository = fileRepository; 23 | } 24 | 25 | @Override 26 | public File saveFile(File file) { 27 | return fileRepository.save(file); 28 | } 29 | 30 | @Override 31 | public void removeFile(String id) { 32 | fileRepository.deleteById(id); 33 | } 34 | 35 | @Override 36 | public File getFileById(String id) { 37 | return fileRepository.findById(id).orElse(null); 38 | } 39 | 40 | @Override 41 | public List listFiles() { 42 | return fileRepository.findAll(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /fileserver/src/main/java/com/pjb/fileserver/util/MD5Util.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver.util; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | /** 9 | * MD5 工具类. 10 | */ 11 | public class MD5Util { 12 | private MD5Util() { 13 | 14 | } 15 | 16 | /** 17 | * 获取该输入流的MD5值 18 | */ 19 | public static String getMD5(InputStream is) throws NoSuchAlgorithmException, IOException { 20 | StringBuilder md5 = new StringBuilder(); 21 | MessageDigest md = MessageDigest.getInstance("MD5"); 22 | byte[] dataBytes = new byte[1024]; 23 | 24 | int nread; 25 | while ((nread = is.read(dataBytes)) != -1) { 26 | md.update(dataBytes, 0, nread); 27 | } 28 | byte[] mdbytes = md.digest(); 29 | for (byte mdbyte : mdbytes) { 30 | md5.append(Integer.toString((mdbyte & 0xff) + 0x100, 16).substring(1)); 31 | } 32 | return md5.toString(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /fileserver/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | spring: 4 | thymeleaf: 5 | cache: false 6 | mode: HTML 7 | servlet: 8 | multipart: 9 | max-file-size: 10MB 10 | max-request-size: 100MB -------------------------------------------------------------------------------- /fileserver/src/main/resources/static/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JinBinPeng/SpringBoot-Business-Blog/336e6ae6ff4058c7c29ac9bf597b621e8620bafb/fileserver/src/main/resources/static/timg.jpg -------------------------------------------------------------------------------- /fileserver/src/main/resources/templates/fragments/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Thymeleaf in action 6 | 7 | 8 |
9 | Welcome to waylau.com 10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /fileserver/src/main/resources/templates/fragments/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Thymeleaf in action 6 | 7 | 8 |
9 |

File server

10 | 首页 11 |
12 | 13 | -------------------------------------------------------------------------------- /fileserver/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 上传文件 5 | 6 |
...
7 | 8 | 9 |
10 |

11 |

12 | 13 |
14 |
15 | 16 | 17 | 18 |
File to upload:
19 | 20 |
21 | 22 | 23 |
24 | 25 | 26 |
27 |
28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
NameIDcontentTypesizeuploadDatemd5
没有文件信息!!
58 |
59 | 60 |
...
61 | 62 | 63 | -------------------------------------------------------------------------------- /fileserver/src/test/java/com/pjb/fileserver/FileserverApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.pjb.fileserver; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class FileserverApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /import.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO user (id, username, password, name, email) VALUES (1, 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '老卫', 'i@waylau.com'); 2 | INSERT INTO user (id, username, password, name, email) VALUES (2, 'waylau', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'Way Lau', 'waylau@waylau.com'); 3 | 4 | INSERT INTO authority (id, name) VALUES (1, 'ROLE_ADMIN'); 5 | INSERT INTO authority (id, name) VALUES (2, 'ROLE_USER'); 6 | 7 | INSERT INTO user_authority (user_id, authority_id) VALUES (1, 1); 8 | INSERT INTO user_authority (user_id, authority_id) VALUES (2, 2); 9 | --------------------------------------------------------------------------------