Skip to content

Commit 68bb5bd

Browse files
yyyCodeclaude
andcommitted
feat: complete feedback and comment management
- Delete stub InterviewController (501-only endpoints) - ConsoleFeedbackView: replace mock data with real fetchPendingFeedback API - Add CommentAdminController (GET /api/v1/admin/comments) - Add CommentService.listAllComments with batch user/article loading - ConsoleCommentsView: real comment table with status filter and pagination - Add comment-admin.js API module Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 07fcb6b commit 68bb5bd

6 files changed

Lines changed: 353 additions & 52 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.yqz.openblog.comment.controller;
2+
3+
import com.yqz.openblog.comment.dto.CommentAdminResponse;
4+
import com.yqz.openblog.comment.service.CommentService;
5+
import com.yqz.openblog.common.ApiResponse;
6+
import com.yqz.openblog.common.PageResult;
7+
import org.springframework.security.access.prepost.PreAuthorize;
8+
import org.springframework.web.bind.annotation.*;
9+
10+
@RestController
11+
@RequestMapping("/api/v1/admin/comments")
12+
@CrossOrigin(origins = "*")
13+
public class CommentAdminController {
14+
15+
private final CommentService commentService;
16+
17+
public CommentAdminController(CommentService commentService) {
18+
this.commentService = commentService;
19+
}
20+
21+
@GetMapping
22+
@PreAuthorize("hasRole('ADMIN')")
23+
public ApiResponse<PageResult<CommentAdminResponse>> list(
24+
@RequestParam(defaultValue = "0") int page,
25+
@RequestParam(defaultValue = "50") int size,
26+
@RequestParam(required = false) String status) {
27+
return ApiResponse.ok(commentService.listAllComments(page, size, status));
28+
}
29+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.yqz.openblog.comment.dto;
2+
3+
import java.time.Instant;
4+
5+
/**
6+
* 管理后台评论列表项(扁平结构,含文章标题)。
7+
*/
8+
public class CommentAdminResponse {
9+
10+
private Long id;
11+
private Long articleId;
12+
private String articleTitle;
13+
private Long userId;
14+
private String userName;
15+
private String content;
16+
private String status;
17+
private Instant createdAt;
18+
19+
public Long getId() {
20+
return id;
21+
}
22+
23+
public void setId(Long id) {
24+
this.id = id;
25+
}
26+
27+
public Long getArticleId() {
28+
return articleId;
29+
}
30+
31+
public void setArticleId(Long articleId) {
32+
this.articleId = articleId;
33+
}
34+
35+
public String getArticleTitle() {
36+
return articleTitle;
37+
}
38+
39+
public void setArticleTitle(String articleTitle) {
40+
this.articleTitle = articleTitle;
41+
}
42+
43+
public Long getUserId() {
44+
return userId;
45+
}
46+
47+
public void setUserId(Long userId) {
48+
this.userId = userId;
49+
}
50+
51+
public String getUserName() {
52+
return userName;
53+
}
54+
55+
public void setUserName(String userName) {
56+
this.userName = userName;
57+
}
58+
59+
public String getContent() {
60+
return content;
61+
}
62+
63+
public void setContent(String content) {
64+
this.content = content;
65+
}
66+
67+
public String getStatus() {
68+
return status;
69+
}
70+
71+
public void setStatus(String status) {
72+
this.status = status;
73+
}
74+
75+
public Instant getCreatedAt() {
76+
return createdAt;
77+
}
78+
79+
public void setCreatedAt(Instant createdAt) {
80+
this.createdAt = createdAt;
81+
}
82+
}

OpenBlog-business/src/main/java/com/yqz/openblog/comment/service/CommentService.java

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
77
import com.yqz.openblog.article.entity.Article;
88
import com.yqz.openblog.article.entity.ArticleStatus;
9+
import com.yqz.openblog.comment.dto.CommentAdminResponse;
910
import com.yqz.openblog.comment.dto.CommentCreateRequest;
1011
import com.yqz.openblog.comment.dto.CommentThreadResponse;
1112
import com.yqz.openblog.comment.dto.CommentUserResponse;
1213
import com.yqz.openblog.comment.entity.Comment;
1314
import com.yqz.openblog.comment.entity.CommentStatus;
15+
import com.yqz.openblog.article.entity.Article;
1416
import com.yqz.openblog.article.repo.ArticleMapper;
1517
import com.yqz.openblog.comment.repo.CommentMapper;
1618
import com.yqz.openblog.common.BizException;
@@ -19,13 +21,8 @@
1921
import com.yqz.openblog.user.repo.UserRepository;
2022
import org.springframework.stereotype.Service;
2123

22-
import java.util.ArrayList;
23-
import java.util.Comparator;
24-
import java.util.HashMap;
25-
import java.util.HashSet;
26-
import java.util.List;
27-
import java.util.Map;
28-
import java.util.Set;
24+
import java.util.*;
25+
import java.util.stream.Collectors;
2926

3027
@Service
3128
public class CommentService {
@@ -267,6 +264,45 @@ public void deleteComment(Long commentId, Long uid, boolean isAdmin) {
267264
}
268265
}
269266

267+
/**
268+
* 管理后台全站评论列表(扁平、按时间倒序)。
269+
*/
270+
public PageResult<CommentAdminResponse> listAllComments(int page, int size, String statusFilter) {
271+
LambdaQueryWrapper<Comment> w = Wrappers.lambdaQuery(Comment.class)
272+
.orderByDesc(Comment::getCreatedAt);
273+
if (statusFilter != null && !statusFilter.isBlank()) {
274+
w.eq(Comment::getStatus, CommentStatus.valueOf(statusFilter));
275+
}
276+
277+
IPage<Comment> p = commentMapper.selectPage(new Page<>(page + 1, size), w);
278+
279+
// 批量加载用户和文章
280+
Set<Long> userIds = p.getRecords().stream().map(Comment::getUserId).filter(Objects::nonNull).collect(Collectors.toSet());
281+
Set<Long> articleIds = p.getRecords().stream().map(Comment::getArticleId).filter(Objects::nonNull).collect(Collectors.toSet());
282+
283+
Map<Long, User> userMap = userIds.isEmpty() ? Collections.emptyMap() :
284+
userRepository.findAllById(userIds).stream().collect(Collectors.toMap(User::getId, u -> u, (a, b) -> a));
285+
Map<Long, Article> articleMap = articleIds.isEmpty() ? Collections.emptyMap() :
286+
articleMapper.selectBatchIds(articleIds).stream().collect(Collectors.toMap(Article::getId, a -> a, (a, b) -> a));
287+
288+
List<CommentAdminResponse> items = p.getRecords().stream().map(c -> {
289+
CommentAdminResponse r = new CommentAdminResponse();
290+
r.setId(c.getId());
291+
r.setArticleId(c.getArticleId());
292+
Article article = articleMap.get(c.getArticleId());
293+
r.setArticleTitle(article != null ? article.getTitle() : null);
294+
r.setUserId(c.getUserId());
295+
User user = userMap.get(c.getUserId());
296+
r.setUserName(user != null ? user.getUsername() : null);
297+
r.setContent(c.getContent());
298+
r.setStatus(c.getStatus().name());
299+
r.setCreatedAt(c.getCreatedAt());
300+
return r;
301+
}).collect(Collectors.toList());
302+
303+
return new PageResult<>(items, page, size, p.getTotal());
304+
}
305+
270306
private void ensureUserActive(Long uid) {
271307
User u = userRepository.findById(uid).orElseThrow(() -> new BizException(4041, "用户不存在"));
272308
if ("BANNED".equals(u.getStatus())) {

vue/src/api/comment-admin.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { request } from './http'
2+
3+
export function fetchAllComments(page = 0, size = 50, status) {
4+
let url = `/api/v1/admin/comments?page=${page}&size=${size}`
5+
if (status) url += `&status=${encodeURIComponent(status)}`
6+
return request(url, { method: 'GET', withAuth: true })
7+
}

0 commit comments

Comments
 (0)