问答平台(3),私信列表

私信列表

1
2
- 查询当前用户的会话列表,每个会话只显示一条最新的私信。
- 支持分页显示

私信详情

1
2
- 查询某个会话所包含的私信。
- 支持分页显示

实体类

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
// Message.java
public class Message {

private int id;
private int fromId;
private int toId;
private String conversationId;
private String content;
private int status;
private Date createTime;

public int getId() {
return id;
}

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

public int getFromId() {
return fromId;
}

public void setFromId(int fromId) {
this.fromId = fromId;
}

public int getToId() {
return toId;
}

public void setToId(int toId) {
this.toId = toId;
}

public String getConversationId() {
return conversationId;
}

public void setConversationId(String conversationId) {
this.conversationId = conversationId;
}

public String getContent() {
return content;
}

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

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;
}

@Override
public String toString() {
return "Message{" +
"id=" + id +
", fromId=" + fromId +
", toId=" + toId +
", conversationId='" + conversationId + '\'' +
", content='" + content + '\'' +
", status=" + status +
", createTime=" + createTime +
'}';
}
}

数据访问层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// MessageMapper.java
@Mapper
@Repository
public interface MessageMapper {

// 查询当前用户的会话列表,针对每个会话只返回一条最新的私信
List<Message> selectConversations(int userId, int offset, int limit);

// 查询当前用户的会话数量
int selectConversationCount(int userId);

// 查询某个会话所包含的私信列表
List<Message> selectLetters(String conversationId, int offset, int limit);

// 查询某个会话所包含的私信数量
int selectLetterCount(String conversationId);

// 查询未读私信的数量
int selectLetterUnreadCount(int userId, String conversationId);
}
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
<!-- message-mapper.xml -->
<sql id="selectFields">
id, from_id, to_id, conversation_id, content, status, create_time
</sql>

<select id="selectConversations" resultType="Message">
select
<include refid="selectFields"></include>
from message
where id in (
select max(id) from message
where status != 2
and from_id != 1
and (from_id = #{userId} or to_id = #{userId})
group by conversation_id
)
order by id desc
limit #{offset}, #{limit}
</select>

<select id="selectConversationCount" resultType="int">
select count(m.maxid) from (
select max(id) as maxid from message
where status != 2
and from_id != 1
and (from_id = #{userId} or to_id = #{userId})
group by conversation_id
) as m
</select>

<select id="selectLetters" resultType="Message">
select
<include refid="selectFields"></include>
from message
where status != 2
and from_id != 1
and conversation_id = #{conversationId}
order by id desc
limit #{offset}, #{limit}
</select>

<select id="selectLetterCount" resultType="int">
select count(id)
from message
where status != 2
and from_id != 1
and conversation_id = #{conversationId}
</select>

<select id="selectLetterUnreadCount" resultType="int">
select count(id)
from message
where status = 0
and from_id != 1
and to_id = #{userId}
<if test="conversationId!=null">
and conversation_id = #{conversationId}
</if>
</select>

测试类

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
// MapperTests.java
@Autowired
private MessageMapper messageMapper;

@Test
public void testSelectLetters() {
List<Message> messageList = messageMapper.selectConversations(111, 0, 20);
for (Message message : messageList) {
System.out.println(message);
}

int count = messageMapper.selectConversationCount(111);
System.out.println(count);

messageList = messageMapper.selectLetters("111_112", 0, 10);
for (Message message : messageList) {
System.out.println(message);
}

count = messageMapper.selectLetterCount("111_112");
System.out.println(count);

count = messageMapper.selectLetterUnreadCount(131, "111_131");
System.out.println(count);
}

业务层

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
// MessageService.java
@Service
public class MessageService {

@Autowired
private MessageMapper messageMapper;

public List<Message> findConversations(int userId, int offset, int limit) {
return messageMapper.selectConversations(userId, offset, limit);
}

public int findConversationCount(int userId) {
return messageMapper.selectConversationCount(userId);
}

public List<Message> findLetters(String conversationId, int offset, int limit) {
return messageMapper.selectLetters(conversationId, offset, limit);
}

public int findLetterCount(String conversationId) {
return messageMapper.selectLetterCount(conversationId);
}

public int findLetterUnreadCount(int userId, String conversationId) {
return messageMapper.selectLetterUnreadCount(userId, conversationId);
}
}

表现层

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
// MessageController.java
@Controller
public class MessageController {

@Autowired
private MessageService messageService;

@Autowired
private HostHolder hostHolder;

@Autowired
private UserService userService;

// 私信列表
@RequestMapping(path = "/letter/list", method = RequestMethod.GET)
public String getLetterList(Model model, Page page) {
User user = hostHolder.getUser();
// 分页信息
page.setLimit(5);
page.setPath("/letter/list");
page.setRows(messageService.findConversationCount(user.getId()));
// 会话列表
List<Message> conversationList = messageService.findConversations(
user.getId(), page.getOffset(), page.getLimit());
List<Map<String, Object>> conversations = new ArrayList<>();
if (conversationList != null) {
for (Message message : conversationList) {
Map<String, Object> map = new HashMap<>();
map.put("conversation", message);
map.put("letterCount", messageService.findLetterCount(message.getConversationId()));
map.put("unreadCount", messageService.findLetterUnreadCount(user.getId(), message.getConversationId()));
int targetId = user.getId() == message.getFromId() ? message.getToId() : message.getFromId();
map.put("target", userService.findUserById(targetId));
conversations.add(map);
}
}
model.addAttribute("conversations", conversations);
// 查询未读消息数量
int letterUnreadCount = messageService.findLetterUnreadCount(user.getId(), null);
model.addAttribute("letterUnreadCount", letterUnreadCount);

return "/site/letter";
}

@RequestMapping(path = "/letter/detail/{conversationId}", method = RequestMethod.GET)
public String getLetterDetail(@PathVariable("conversationId") String conversationId, Page page, Model model) {
// 分页信息
page.setLimit(5);
page.setPath("/letter/detail/" + conversationId);
page.setRows(messageService.findLetterCount(conversationId));
// 私信列表
List<Message> letterList = messageService.findLetters(conversationId, page.getOffset(), page.getLimit());
List<Map<String, Object>> letters = new ArrayList<>();
if (letterList != null) {
for (Message message : letterList) {
Map<String, Object> map = new HashMap<>();
map.put("letter", message);
map.put("fromUser", userService.findUserById(message.getFromId()));
letters.add(map);
}
}
model.addAttribute("letters", letters);
// 私信目标
model.addAttribute("target", getLetterTarget(conversationId));
// 设置已读
List<Integer> ids = getLetterIds(letterList);
if (!ids.isEmpty()) {
messageService.readMessage(ids);
}

return "/site/letter-detail";
}

private User getLetterTarget(String conversationId) {
String[] ids = conversationId.split("_");
int id0 = Integer.parseInt(ids[0]);
int id1 = Integer.parseInt(ids[1]);

if (hostHolder.getUser().getId() == id0) {
return userService.findUserById(id1);
} else {
return userService.findUserById(id0);
}
}
}

页面

  • index.html:处理消息的链接
    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 功能 -->
    <li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser!=null}">
    <a class="nav-link position-relative" th:href="@{/letter/list}">消息
    <span class="badge badge-danger"
    th:text="${allUnreadCount!=0?allUnreadCount:''}">12
    </span>
    </a>
    </li>
  • letter.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
    <!-- 内容 -->
    <!-- 选项 -->
    <li class="nav-item">
    <a class="nav-link position-relative active" th:href="@{/letter/list}">
    朋友私信<span class="badge badge-danger" th:text="${letterUnreadCount}"
    th:if="${letterUnreadCount!=0}">3</span>
    </a>
    </li>
    <!-- 私信列表 -->
    <ul class="list-unstyled">
    <li class="media pb-3 pt-3 mb-3 border-bottom position-relative" th:each="map:${conversations}">
    <span class="badge badge-danger" th:text="${map.unreadCount}" th:if="${map.unreadCount!=0}">3</span>
    <a href="profile.html">
    <img th:src=" ${map.target.headerUrl}" class="mr-4 rounded-circle user-header" alt="用户头像">
    </a>
    <div class="media-body">
    <h6 class="mt-0 mb-3">
    <span class="text-success" th:utext="${map.target.username}">落基山脉下的闲人</span>
    <span class="float-right text-muted font-size-12"
    th:text="${#dates.format(map.conversation.createTime, 'yyyy-MM-dd HH:mm:ss')}">2019-04-28 14:13:25
    </span>
    </h6>
    <div>
    <a th:href="@{|/letter/detail/${map.conversation.conversationId}|}"
    th:utext="${map.conversation.content}">米粉车, 你来吧!
    </a>
    <ul class="d-inline font-size-12 float-right">
    <li class="d-inline ml-2">
    <a href="#" class="text-primary">
    <i th:text="${map.letterCount}">5</i>条会话
    </a>
    </li>
    </ul>
    </div>
    </div>
    </li>
    </ul>
  • letter-detail.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
    <!-- 内容 -->
    <h6>
    <b class="square"></b> 来自 <i class="text-success" th:utext="${target.username}">落基山脉下的闲人</i> 的私信
    </h6>
    <div class="col-4 text-right">
    <button type="button" class="btn btn-secondary btn-sm" onclick="back();">返回</button>
    <button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#sendModal">
    给TA私信
    </button>
    </div>
    <!-- 私信列表 -->
    <ul class="list-unstyled mt-4">
    <li class="media pb-3 pt-3 mb-2" th:each="map:${letters}">
    <a href="profile.html">
    <img th:src="${map.fromUser.headerUrl}" class="mr-4 rounded-circle user-header" alt="用户头像">
    </a>
    <div class="toast show d-lg-block" role="alert" aria-live="assertive" aria-atomic="true">
    <div class="toast-header">
    <strong class="mr-auto" th:utext="${map.fromUser.username}">落基山脉下的闲人</strong>
    <small th:text="${#dates.format(map.letter.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-25 15:49:32</small>
    <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
    <span aria-hidden="true">&times;</span>
    </button>
    </div>
    <div class="toast-body" th:utext="${map.letter.content}">
    君不见, 黄河之水天上来, 奔流到海不复回!
    </div>
    </div>
    </li>
    </ul>
    <!-- js -->
    <script>
    function back() {
    location.href = CONTEXT_PATH + "/letter/list";
    }
    </script>

问答平台(3),私信列表
https://lcf163.github.io/2020/05/16/问答平台(3),私信列表/
作者
乘风的小站
发布于
2020年5月16日
许可协议