Skip to content

Commit 9c761bb

Browse files
committed
feat(admin): implement admin and report endpoints
1 parent 75636de commit 9c761bb

File tree

59 files changed

+1898
-1576
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1898
-1576
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.bounswe.jobboardbackend.admin.controller;
2+
3+
import jakarta.validation.Valid;
4+
import lombok.RequiredArgsConstructor;
5+
import org.bounswe.jobboardbackend.admin.dto.AdminActionResponse;
6+
import org.bounswe.jobboardbackend.report.dto.ReportResponse;
7+
import org.bounswe.jobboardbackend.report.dto.ResolveReportRequest;
8+
import org.bounswe.jobboardbackend.report.model.enums.ReportableEntityType;
9+
import org.bounswe.jobboardbackend.report.model.enums.ReportStatus;
10+
import org.bounswe.jobboardbackend.admin.service.AdminReportService;
11+
import org.bounswe.jobboardbackend.report.service.ReportService;
12+
import org.springframework.data.domain.Page;
13+
import org.springframework.data.domain.Pageable;
14+
import org.springframework.data.web.PageableDefault;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.security.access.prepost.PreAuthorize;
17+
import org.springframework.web.bind.annotation.*;
18+
19+
@RestController
20+
@RequestMapping("/api/admin/reports")
21+
@RequiredArgsConstructor
22+
@PreAuthorize("hasRole('ROLE_ADMIN')")
23+
public class AdminReportController {
24+
25+
private final ReportService reportService;
26+
private final AdminReportService adminReportService;
27+
28+
@GetMapping
29+
public ResponseEntity<Page<ReportResponse>> listReports(
30+
@RequestParam(required = false) ReportStatus status,
31+
@RequestParam(required = false) ReportableEntityType entityType,
32+
@PageableDefault(size = 20) Pageable pageable) {
33+
34+
Page<ReportResponse> reports = reportService.listReports(status, entityType, pageable);
35+
return ResponseEntity.ok(reports);
36+
}
37+
38+
@GetMapping("/{id}")
39+
public ResponseEntity<ReportResponse> getReport(@PathVariable Long id) {
40+
ReportResponse report = reportService.getReport(id);
41+
return ResponseEntity.ok(report);
42+
}
43+
44+
@PostMapping("/{id}/resolve")
45+
public ResponseEntity<AdminActionResponse> resolveReport(
46+
@PathVariable Long id,
47+
@Valid @RequestBody ResolveReportRequest request) {
48+
49+
adminReportService.resolveReport(id, request);
50+
return ResponseEntity.ok(new AdminActionResponse("Report resolved successfully"));
51+
}
52+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.bounswe.jobboardbackend.admin.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
6+
@Data
7+
@AllArgsConstructor
8+
public class AdminActionResponse {
9+
private String message;
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.bounswe.jobboardbackend.admin.dto;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class BanMentorRequest {
7+
private String reason;
8+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.bounswe.jobboardbackend.admin.dto;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Size;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NoArgsConstructor;
9+
10+
@Data
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class BanUserRequest {
15+
16+
@NotBlank(message = "Ban reason must not be blank")
17+
@Size(max = 500, message = "Ban reason must be at most 500 characters")
18+
private String reason;
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.bounswe.jobboardbackend.admin.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class DeleteContentRequest {
13+
private String reason;
14+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.bounswe.jobboardbackend.admin.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
import org.bounswe.jobboardbackend.auth.model.Role;
8+
9+
import java.time.Instant;
10+
11+
@Data
12+
@Builder
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
public class UserListResponse {
16+
17+
private Long id;
18+
private String username;
19+
private String email;
20+
private Role role;
21+
private Boolean isBanned;
22+
private String banReason;
23+
private Instant createdAt;
24+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.bounswe.jobboardbackend.admin.service;
2+
3+
import jakarta.transaction.Transactional;
4+
import lombok.RequiredArgsConstructor;
5+
import org.bounswe.jobboardbackend.exception.ErrorCode;
6+
import org.bounswe.jobboardbackend.exception.HandleException;
7+
import org.bounswe.jobboardbackend.forum.model.ForumComment;
8+
import org.bounswe.jobboardbackend.forum.model.ForumPost;
9+
import org.bounswe.jobboardbackend.forum.repository.ForumCommentRepository;
10+
import org.bounswe.jobboardbackend.forum.repository.ForumPostRepository;
11+
import org.springframework.stereotype.Service;
12+
13+
@Service
14+
@RequiredArgsConstructor
15+
public class AdminForumService {
16+
17+
private final ForumPostRepository forumPostRepository;
18+
private final ForumCommentRepository forumCommentRepository;
19+
20+
@Transactional
21+
public void deletePost(Long postId, String reason) {
22+
ForumPost post = forumPostRepository.findById(postId)
23+
.orElseThrow(() -> new HandleException(ErrorCode.NOT_FOUND, "Forum post not found"));
24+
25+
// Cascade delete will handle comments
26+
forumPostRepository.delete(post);
27+
28+
// TODO: Log deletion with reason for audit
29+
}
30+
31+
@Transactional
32+
public void deleteComment(Long commentId, String reason) {
33+
ForumComment comment = forumCommentRepository.findById(commentId)
34+
.orElseThrow(() -> new HandleException(ErrorCode.NOT_FOUND, "Forum comment not found"));
35+
36+
forumCommentRepository.delete(comment);
37+
38+
// TODO: Log deletion with reason for audit
39+
}
40+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.bounswe.jobboardbackend.admin.service;
2+
3+
import jakarta.transaction.Transactional;
4+
import lombok.RequiredArgsConstructor;
5+
import org.bounswe.jobboardbackend.exception.ErrorCode;
6+
import org.bounswe.jobboardbackend.exception.HandleException;
7+
import org.bounswe.jobboardbackend.jobapplication.model.JobApplication;
8+
import org.bounswe.jobboardbackend.jobapplication.repository.JobApplicationRepository;
9+
import org.springframework.stereotype.Service;
10+
11+
@Service
12+
@RequiredArgsConstructor
13+
public class AdminJobApplicationService {
14+
15+
private final JobApplicationRepository jobApplicationRepository;
16+
17+
@Transactional
18+
public void deleteJobApplication(Long applicationId, String reason) {
19+
JobApplication application = jobApplicationRepository.findById(applicationId)
20+
.orElseThrow(
21+
() -> new HandleException(ErrorCode.JOB_APPLICATION_NOT_FOUND, "Job application not found"));
22+
23+
jobApplicationRepository.delete(application);
24+
}
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.bounswe.jobboardbackend.admin.service;
2+
3+
import jakarta.transaction.Transactional;
4+
import lombok.RequiredArgsConstructor;
5+
import org.bounswe.jobboardbackend.exception.ErrorCode;
6+
import org.bounswe.jobboardbackend.exception.HandleException;
7+
import org.bounswe.jobboardbackend.jobpost.model.JobPost;
8+
import org.bounswe.jobboardbackend.jobpost.repository.JobPostRepository;
9+
import org.bounswe.jobboardbackend.jobapplication.repository.JobApplicationRepository;
10+
import org.springframework.stereotype.Service;
11+
12+
@Service
13+
@RequiredArgsConstructor
14+
public class AdminJobPostService {
15+
16+
private final JobPostRepository jobPostRepository;
17+
private final JobApplicationRepository jobApplicationRepository;
18+
19+
@Transactional
20+
public void deleteJobPost(Long jobPostId, String reason) {
21+
JobPost jobPost = jobPostRepository.findById(jobPostId)
22+
.orElseThrow(() -> new HandleException(ErrorCode.JOB_POST_NOT_FOUND, "Job post not found"));
23+
24+
// Delete related job applications first (cascade)
25+
jobApplicationRepository.deleteAllByJobPostId(jobPostId);
26+
27+
jobPostRepository.delete(jobPost);
28+
29+
// TODO: Log deletion with reason for audit
30+
}
31+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.bounswe.jobboardbackend.admin.service;
2+
3+
import jakarta.transaction.Transactional;
4+
import lombok.RequiredArgsConstructor;
5+
6+
import org.bounswe.jobboardbackend.auth.model.User;
7+
import org.bounswe.jobboardbackend.auth.repository.UserRepository;
8+
import org.bounswe.jobboardbackend.exception.ErrorCode;
9+
import org.bounswe.jobboardbackend.exception.HandleException;
10+
import org.bounswe.jobboardbackend.mentorship.service.MentorshipService;
11+
import org.springframework.stereotype.Service;
12+
import org.bounswe.jobboardbackend.mentorship.model.MentorProfile;
13+
import org.bounswe.jobboardbackend.mentorship.repository.MentorProfileRepository;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class AdminMentorService {
18+
19+
private final MentorProfileRepository mentorProfileRepository;
20+
private final UserRepository userRepository;
21+
private final MentorshipService mentorshipService;
22+
23+
@Transactional
24+
public void deleteMentor(Long mentorProfileId, String reason) {
25+
// Get mentor to extract userId
26+
MentorProfile mentorProfile = mentorProfileRepository.findById(mentorProfileId)
27+
.orElseThrow(() -> new HandleException(ErrorCode.NOT_FOUND, "Mentor profile not found"));
28+
Long userId = mentorProfile.getUser().getId();
29+
30+
// Ban user as mentor
31+
User user = userRepository.findById(userId)
32+
.orElseThrow(() -> new HandleException(ErrorCode.USER_NOT_FOUND, "User not found"));
33+
user.setIsMentorBanned(true);
34+
user.setMentorBanReason(reason);
35+
userRepository.save(user);
36+
userRepository.flush(); // Ensure immediate database update
37+
38+
// Delete mentor profile
39+
mentorshipService.deleteMentorProfile(userId);
40+
41+
// TODO: Log deletion with reason for audit
42+
}
43+
}

0 commit comments

Comments
 (0)