mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-04-25 05:17:35 +08:00
修复问题#927,#860
1.优化评论请求,将两个请求合并为一个 2.修改个人主页按钮的主次
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
package com.openisle.controller;
|
package com.openisle.controller;
|
||||||
|
|
||||||
|
import com.openisle.dto.PostChangeLogDto;
|
||||||
|
import com.openisle.dto.TimelineItemDto;
|
||||||
|
import com.openisle.mapper.PostChangeLogMapper;
|
||||||
import com.openisle.model.Comment;
|
import com.openisle.model.Comment;
|
||||||
import com.openisle.dto.CommentDto;
|
import com.openisle.dto.CommentDto;
|
||||||
import com.openisle.dto.CommentRequest;
|
import com.openisle.dto.CommentRequest;
|
||||||
import com.openisle.mapper.CommentMapper;
|
import com.openisle.mapper.CommentMapper;
|
||||||
import com.openisle.service.CaptchaService;
|
import com.openisle.model.CommentSort;
|
||||||
import com.openisle.service.CommentService;
|
import com.openisle.service.*;
|
||||||
import com.openisle.service.LevelService;
|
|
||||||
import com.openisle.service.PointService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -21,6 +22,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -34,6 +37,8 @@ public class CommentController {
|
|||||||
private final CaptchaService captchaService;
|
private final CaptchaService captchaService;
|
||||||
private final CommentMapper commentMapper;
|
private final CommentMapper commentMapper;
|
||||||
private final PointService pointService;
|
private final PointService pointService;
|
||||||
|
private final PostChangeLogService changeLogService;
|
||||||
|
private final PostChangeLogMapper postChangeLogMapper;
|
||||||
|
|
||||||
@Value("${app.captcha.enabled:false}")
|
@Value("${app.captcha.enabled:false}")
|
||||||
private boolean captchaEnabled;
|
private boolean captchaEnabled;
|
||||||
@@ -85,15 +90,43 @@ public class CommentController {
|
|||||||
@GetMapping("/posts/{postId}/comments")
|
@GetMapping("/posts/{postId}/comments")
|
||||||
@Operation(summary = "List comments", description = "List comments for a post")
|
@Operation(summary = "List comments", description = "List comments for a post")
|
||||||
@ApiResponse(responseCode = "200", description = "Comments",
|
@ApiResponse(responseCode = "200", description = "Comments",
|
||||||
content = @Content(array = @ArraySchema(schema = @Schema(implementation = CommentDto.class))))
|
content = @Content(array = @ArraySchema(schema = @Schema(implementation = TimelineItemDto.class))))
|
||||||
public List<CommentDto> listComments(@PathVariable Long postId,
|
public List<TimelineItemDto<?>> listComments(@PathVariable Long postId,
|
||||||
@RequestParam(value = "sort", required = false, defaultValue = "OLDEST") com.openisle.model.CommentSort sort) {
|
@RequestParam(value = "sort", required = false, defaultValue = "OLDEST") CommentSort sort) {
|
||||||
log.debug("listComments called for post {} with sort {}", postId, sort);
|
log.debug("listComments called for post {} with sort {}", postId, sort);
|
||||||
List<CommentDto> list = commentService.getCommentsForPost(postId, sort).stream()
|
List<CommentDto> commentDtoList = commentService.getCommentsForPost(postId, sort).stream()
|
||||||
.map(commentMapper::toDtoWithReplies)
|
.map(commentMapper::toDtoWithReplies)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
log.debug("listComments returning {} comments", list.size());
|
List<PostChangeLogDto> postChangeLogDtoList = changeLogService.listLogs(postId).stream()
|
||||||
return list;
|
.map(postChangeLogMapper::toDto)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<TimelineItemDto<?>> itemDtoList = new ArrayList<>();
|
||||||
|
|
||||||
|
itemDtoList.addAll(commentDtoList.stream()
|
||||||
|
.map(c -> new TimelineItemDto<>(
|
||||||
|
c.getId(),
|
||||||
|
"comment",
|
||||||
|
c.getCreatedAt(),
|
||||||
|
c // payload 是 CommentDto
|
||||||
|
))
|
||||||
|
.toList());
|
||||||
|
|
||||||
|
itemDtoList.addAll(postChangeLogDtoList.stream()
|
||||||
|
.map(l -> new TimelineItemDto<>(
|
||||||
|
l.getId(),
|
||||||
|
"log",
|
||||||
|
l.getTime(), // 注意字段名不一样
|
||||||
|
l // payload 是 PostChangeLogDto
|
||||||
|
))
|
||||||
|
.toList());
|
||||||
|
// 排序
|
||||||
|
Comparator<TimelineItemDto<?>> comparator = Comparator.comparing(TimelineItemDto::getCreatedAt);
|
||||||
|
if (CommentSort.NEWEST.equals(sort)) {
|
||||||
|
comparator = comparator.reversed();
|
||||||
|
}
|
||||||
|
itemDtoList.sort(comparator);
|
||||||
|
log.debug("listComments returning {} comments", itemDtoList.size());
|
||||||
|
return itemDtoList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/comments/{id}")
|
@DeleteMapping("/comments/{id}")
|
||||||
|
|||||||
20
backend/src/main/java/com/openisle/dto/TimelineItemDto.java
Normal file
20
backend/src/main/java/com/openisle/dto/TimelineItemDto.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package com.openisle.dto;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* comment and change_log Dto
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class TimelineItemDto<T> {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String kind; // "comment" | "log"
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private T payload; // 泛型,具体类型由外部决定
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
--primary-color-hover: rgb(9, 95, 105);
|
--primary-color-hover: rgb(9, 95, 105);
|
||||||
--primary-color: rgb(10, 110, 120);
|
--primary-color: rgb(10, 110, 120);
|
||||||
--primary-color-disabled: rgba(93, 152, 156, 0.5);
|
--primary-color-disabled: rgba(93, 152, 156, 0.5);
|
||||||
|
--secondary-color:rgb(255, 255, 255);
|
||||||
|
--secondary-color-hover:rgba(165, 255, 255, 0.447);
|
||||||
--new-post-icon-color: rgba(10, 111, 120, 0.598);
|
--new-post-icon-color: rgba(10, 111, 120, 0.598);
|
||||||
--header-height: 60px;
|
--header-height: 60px;
|
||||||
--header-background-color: white;
|
--header-background-color: white;
|
||||||
|
|||||||
@@ -320,6 +320,7 @@ const mapComment = (
|
|||||||
level = 0,
|
level = 0,
|
||||||
) => ({
|
) => ({
|
||||||
id: c.id,
|
id: c.id,
|
||||||
|
kind: 'comment',
|
||||||
userName: c.author.username,
|
userName: c.author.username,
|
||||||
medal: c.author.displayMedal,
|
medal: c.author.displayMedal,
|
||||||
userId: c.author.id,
|
userId: c.author.id,
|
||||||
@@ -374,6 +375,7 @@ const changeLogIcon = (l) => {
|
|||||||
|
|
||||||
const mapChangeLog = (l) => ({
|
const mapChangeLog = (l) => ({
|
||||||
id: l.id,
|
id: l.id,
|
||||||
|
kind: 'log',
|
||||||
username: l.username,
|
username: l.username,
|
||||||
userAvatar: l.userAvatar,
|
userAvatar: l.userAvatar,
|
||||||
type: l.type,
|
type: l.type,
|
||||||
@@ -788,9 +790,9 @@ const fetchCommentSorts = () => {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchComments = async () => {
|
const fetchCommentsAndChangeLog = async () => {
|
||||||
isFetchingComments.value = true
|
isFetchingComments.value = true
|
||||||
console.debug('Fetching comments', { postId, sort: commentSort.value })
|
console.info('Fetching comments and chang log', { postId, sort: commentSort.value })
|
||||||
try {
|
try {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
@@ -799,11 +801,34 @@ const fetchComments = async () => {
|
|||||||
headers: { Authorization: token ? `Bearer ${token}` : '' },
|
headers: { Authorization: token ? `Bearer ${token}` : '' },
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
console.debug('Fetch comments response status', res.status)
|
console.info('Fetch comments response status', res.status)
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
console.debug('Fetched comments count', data.length)
|
console.info('Fetched comments data', data)
|
||||||
comments.value = data.map(mapComment)
|
|
||||||
|
const commentList = []
|
||||||
|
const changeLogList = []
|
||||||
|
// 时间线列表,包含评论和日志
|
||||||
|
const newTimelineItemList = []
|
||||||
|
|
||||||
|
for (const item of data) {
|
||||||
|
const mappedPayload =
|
||||||
|
item.kind === 'comment'
|
||||||
|
? mapComment(item.payload)
|
||||||
|
: mapChangeLog(item.payload)
|
||||||
|
newTimelineItemList.push(mappedPayload)
|
||||||
|
|
||||||
|
if (item.kind === 'comment') {
|
||||||
|
commentList.push(mappedPayload)
|
||||||
|
} else {
|
||||||
|
changeLogList.push(mappedPayload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comments.value = commentList
|
||||||
|
changeLogs.value = changeLogList
|
||||||
|
timelineItems.value = newTimelineItemList
|
||||||
|
|
||||||
isFetchingComments.value = false
|
isFetchingComments.value = false
|
||||||
await nextTick()
|
await nextTick()
|
||||||
gatherPostItems()
|
gatherPostItems()
|
||||||
@@ -815,37 +840,8 @@ const fetchComments = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchChangeLogs = async () => {
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${API_BASE_URL}/api/posts/${postId}/change-logs`)
|
|
||||||
if (res.ok) {
|
|
||||||
const data = await res.json()
|
|
||||||
changeLogs.value = data.map(mapChangeLog)
|
|
||||||
await nextTick()
|
|
||||||
gatherPostItems()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.debug('Fetch change logs error', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// todo(tim): fetchComments, fetchChangeLogs 整合到一个请求,并且取消前端排序
|
|
||||||
//
|
|
||||||
const fetchTimeline = async () => {
|
const fetchTimeline = async () => {
|
||||||
await Promise.all([fetchComments(), fetchChangeLogs()])
|
await fetchCommentsAndChangeLog()
|
||||||
const cs = comments.value.map((c) => ({ ...c, kind: 'comment' }))
|
|
||||||
const ls = changeLogs.value.map((l) => ({ ...l, kind: 'log' }))
|
|
||||||
|
|
||||||
if (commentSort.value === 'NEWEST') {
|
|
||||||
timelineItems.value = [...cs, ...ls].sort(
|
|
||||||
(a, b) => new Date(b.createdAt) - new Date(a.createdAt),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
timelineItems.value = [...cs, ...ls].sort(
|
|
||||||
(a, b) => new Date(a.createdAt) - new Date(b.createdAt),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(commentSort, async () => {
|
watch(commentSort, async () => {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<reduce-user />
|
<reduce-user />
|
||||||
取消关注
|
取消关注
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isMine" class="profile-page-header-subscribe-button" @click="sendMessage">
|
<div v-if="!isMine" class="profile-page-header-send-mail-button" @click="sendMessage">
|
||||||
<message-one />
|
<message-one />
|
||||||
发私信
|
发私信
|
||||||
</div>
|
</div>
|
||||||
@@ -703,6 +703,26 @@ watch(selectedTab, async (val) => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.profile-page-header-send-mail-button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
margin-top: 15px;
|
||||||
|
width: fit-content;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-page-header-send-mail-button:hover {
|
||||||
|
background-color: var(--secondary-color-hover);
|
||||||
|
}
|
||||||
|
|
||||||
.profile-level-container {
|
.profile-level-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user