问答平台(1),开发社区首页

开发社区首页

开发社区首页-图示

1
2
3
4
5
- 开发流程
- 一次请求的执行过程
- 分步实现
- 开发社区首页,显示前10个帖子
- 开发分页组件,分页显示所有的帖子

实体

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
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// DiscussPost.java
public class DiscussPost {

private int id;

private int userId;

private String title;

private String content;

private int type;

private int status;

private Date createTime;

private int commentCount;

private double score;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public int getUserId() {
return userId;
}

public void setUserId(int userId) {
this.userId = userId;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public int getType() {
return type;
}

public void setType(int type) {
this.type = type;
}

public int getStatus() {
return status;
}

public void setStatus(int status) {
this.status = status;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

public int getCommentCount() {
return commentCount;
}

public void setCommentCount(int commentCount) {
this.commentCount = commentCount;
}

public double getScore() {
return score;
}

public void setScore(double score) {
this.score = score;
}

@Override
public String toString() {
return "DiscussPost{" +
"id=" + id +
", userId=" + userId +
", title='" + title + '\'' +
", content='" + content + '\'' +
", type=" + type +
", status=" + status +
", createTime=" + createTime +
", commentCount=" + commentCount +
", Score=" + score +
'}';
}
}

数据访问层

1
2
3
4
5
6
7
8
9
10
// DiscussPostMapper.java
@Mapper
public interface DiscussPostMapper {

List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);

// @Param注解,用于给参数取别名
// 如果只有一个参数,并且在<if>里使用,则必须加别名。
int selectDiscussPostRows(@Param("userId") int userId);
}
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!-- discusspost-mapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nowcoder.community.dao.DiscussPostMapper">

<sql id="selectFields">
id, user_id, title, content, type, status, create_time, comment_count, score
</sql>

<sql id="insertFields">
user_id, title, content, type, status, create_time, comment_count, score
</sql>

<select id="selectDiscussPosts" resultType="DiscussPost">
select
<include refid="selectFields"></include>
from discuss_post
where status != 2
<if test="userId != 0">
and user_id = #{userId}
</if>
<if test="orderMode==0">
order by type desc, create_time desc
</if>
<!-- <if test="orderMode==1">
order by type desc, score desc, create_time desc
</if> -->
limit #{offset}, #{limit}
</select>

<select id="selectDiscussPostRows" resultType="int">
select count(id)
from discuss_post
where status != 2
<if test="userId != 0">
and user_id = #{userId}
</if>
</select>
</mapper>

业务层

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
26
27
28
29
// DiscussPostService.java
@Service
public class DiscussPostService {

@Autowired
private DiscussPostMapper discussPostMapper;

public List<DiscussPost> findDiscussPosts(int userId, int offset, int limit) {

// return discussPostMapper.selectDiscussPosts(userId, offset, limit, orderMode);
return discussPostMapper.selectDiscussPosts(userId, offset, limit);
}

public int findDiscussPostRows(int userId) {
return discussPostMapper.selectDiscussPostRows(userId);
}
}

// UserService.java
@Service
public class UserService {

@Autowired
private UserMapper userMapper;

public User findUserById(int id) {
return userMapper.selectById(id);
}
}

表现层

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
26
27
28
29
30
31
32
33
34
// HomeController.java
@Controller
public class HomeController {

@Autowired
private DiscussPostService discussPostService;

@Autowired
private UserService userService;

@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model, Page page) {
// 方法调用前,Spring MVC会自动实例化Model和Page,并将Page注入Model
// 所以,在thymeleaf中可以直接访问Page对象中的数据
page.setRows(discussPostService.findDiscussPostRows(0));
page.setPath("/index");

List<DiscussPost> list = discussPostService
.findDiscussPosts(0, page.getOffset(), page.getLimit());
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);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts", discussPosts);

return "/index";
}
}

分页

  • 实体
    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
    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
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    // Page.java
    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 - 1imit
    return (current - 1) * limit;
    }

    /**
    * 获取总页数
    *
    * @return
    */
    public int getTotal() {
    // rows / limit
    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;
    }
    }

页面

  • index.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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    <!-- 帖子列表 -->
    <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>
    <!-- 分页 -->
    <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>

问答平台(1),开发社区首页
https://lcf163.github.io/2020/04/14/问答平台(1),开发社区首页/
作者
乘风的小站
发布于
2020年4月14日
许可协议