仿牛客的bbs(一)

329 阅读3分钟

springboot+redis+mybatis 仿牛客的bbs

功能:注册登录,发布帖子,显示评论,私信列表,redis点赞,关注,热帖排行等

里面几个重要的注解

  • @ResponseBody的作用其实是将java对象转为json格式的数据。

  • @responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。

  • @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。

  • @param注解用于给参数取别名
    *@Value @Value(“#{}”) 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量

  • 用 @Value(“${xxxx}”)注解从配置文件读取值的用法

Kaptcha:防止恶意破解密码、刷票、论坛灌水、刷页
压力测试

里面相关的注意点:
thymeleaf的方式引入静态文件

th:href="@{/css/global.css}"   默认从static目录下找
<link rel="stylesheet" th:href="@{/css/global.css}" />

thymeleaf表示时间

th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}"
<b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}" ></b>

HomeController里设置值

List<DiscussPost> list = discussPostService
        .findDiscussPosts(0, page.getOffset(), page.getLimit(), orderMode);
List<Map<String, Object>> discussPosts = new ArrayList<>();
if (list != null) {
    for (DiscussPost post : list) {
        Map<String, Object> map = new HashMap<>();
        map.put("post", post);
        User user = userService.findUserById(post.getUserId());
        map.put("user", user);

        long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId());
        map.put("likeCount", likeCount);

        discussPosts.add(map);
    }
}
model.addAttribute("discussPosts", discussPosts);
model.addAttribute("orderMode", orderMode);

return "/index";

使用thymeleaf对帖子进行输出 th表达式

<!-- 帖子列表 -->
<ul class="list-unstyled">
   <li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
      <a th:href="@{|/user/profile/${map.user.id}|}">
         <img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;">
      </a>
      <div class="media-body">
         <h6 class="mt-0 mb-3">
            <a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</a>
            <span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}">置顶</span>
            <span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span>
         </h6>
         <div class="text-muted font-size-12">
            <u class="mr-3" th:utext="${map.user.username}">寒江雪</u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b>
            <ul class="d-inline float-right">
               <li class="d-inline ml-2">赞 <span th:text="${map.likeCount}">11</span></li>
               <li class="d-inline ml-2">|</li>
               <li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7</span></li>
            </ul>
         </div>
      </div>                
   </li>
</ul>

分页功能

package com.nowcoder.community.entity;
/**
* 封装分页相关的信息.
*/
public class Page {
    // 当前页码
    private int current = 1;
    // 显示上限
    private int limit = 10;
    // 数据总数(用于计算总页数)
    private int rows;
    // 查询路径(用于复用分页链接)
    private String path;

    public int getCurrent() {
        return current;
    }

    public void setCurrent(int current) {
        if (current >= 1) {
            this.current = current;
        }
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        if (limit >= 1 && limit <= 100) {
            this.limit = limit;
        }
    }

    public int getRows() {
        return rows;
    }

    public void setRows(int rows) {
        if (rows >= 0) {
            this.rows = rows;
        }
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 获取当前页的起始行
     *
     * @return
     */
    public int getOffset() {
        // current * limit - limit
        return (current - 1) * limit;
    }

    /**
     * 获取总页数
     *
     * @return
     */
    public int getTotal() {
        // rows / limit [+1]
        if (rows % limit == 0) {
            return rows / limit;
        } else {
            return rows / limit + 1;
        }
    }

    /**
     * 获取起始页码
     *
     * @return
     */
    public int getFrom() {
        int from = current - 2;
        return from < 1 ? 1 : from;
    }

    /**
     * 获取结束页码
     *
     * @return
     */
    public int getTo() {
        int to = current + 2;
        int total = getTotal();
        return to > total ? total : to;
    }
}

jsp里的分页写法

<nav class="mt-5" th:if="${page.rows>0}" th:fragment="pagination">
   <ul class="pagination justify-content-center">
      <li class="page-item">
         <a class="page-link" th:href="@{${page.path}(current=1)}">首页</a>
      </li>
      <li th:class="|page-item ${page.current==1?'disabled':''}|">
         <a class="page-link" th:href="@{${page.path}(current=${page.current-1})}">上一页</a></li>
      <li th:class="|page-item ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}">
         <a class="page-link" th:href="@{${page.path}(current=${i})}" th:text="${i}">1</a>
      </li>
      <li th:class="|page-item ${page.current==page.total?'disabled':''}|">
         <a class="page-link" th:href="@{${page.path}(current=${page.current+1})}">下一页</a>
      </li>
      <li class="page-item">
         <a class="page-link" th:href="@{${page.path}(current=${page.total})}">末页</a>
      </li>
   </ul>
</nav>

发送邮件
使用JavaMailSender发送邮件
使用thymeleaf发送html邮件
添加pom依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-mail</artifactId>
   <version>2.1.5.RELEASE</version>
</dependency>

在application.properties添加配置

# MailProperties
spring.mail.host=smtp.sina.com
spring.mail.port=465
spring.mail.username=gy2088034881@sina.com
spring.mail.password=1234567
spring.mail.protocol=smtps
spring.mail.properties.mail.smtp.ssl.enable=true

创建一个发送类

@Component
public class MailClient {
    private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
    @Autowired
    private JavaMailSender mailSender;
    @Value("${spring.mail.username}")
    private String from;
    public void sendMail(String to, String subject, String content) {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            mailSender.send(helper.getMimeMessage());
        } catch (MessagingException e) {
            logger.error("发送邮件失败:" + e.getMessage());
        }
    }
}

测试类

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MailTests {
    @Autowired
    private MailClient mailClient;

    @Test
    public void testTextMail() {
        mailClient.sendMail("2222222@nowcoder.com", "TEST", "Welcome.");
    }
    
}

测试的html要放在template下的mail目录下,创建html文件
测试类(使用thymeleaf)

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MailTests {

    @Autowired
    private MailClient mailClient;

    @Autowired
    private TemplateEngine templateEngine;

    @Test
    public void testTextMail() {
        mailClient.sendMail("2222222@nowcoder.com", "TEST", "Welcome.");
    }

    @Test
    public void testHtmlMail() {
        Context context = new Context();
        context.setVariable("username", "sunday");

        String content = templateEngine.process("/mail/demo", context);
        System.out.println(content);

        mailClient.sendMail("22222222@nowcoder.com", "HTML", content);
    }
}

上面是其中的一部分