Skip to content

Commit 0b7b602

Browse files
authored
Merge pull request #22 from selab-hs/feature/10
Feature/10 이슈 넘버 #10 에 대한 게시판 기능 구현하였습니다
2 parents 67a1073 + fdfe7f1 commit 0b7b602

File tree

20 files changed

+530
-126
lines changed

20 files changed

+530
-126
lines changed

src/main/generated/com/demo/domain/QAuthority.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class QAuthority extends EntityPathBase<Authority> {
1919

2020
public static final QAuthority authority = new QAuthority("authority");
2121

22-
public final StringPath authorityName = createString("authorityName");
22+
public final EnumPath<Role> role = createEnum("role", Role.class);
2323

2424
public QAuthority(String variable) {
2525
super(Authority.class, forVariable(variable));

src/main/java/com/demo/config/SecurityConfig.java

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,13 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
7070
})
7171
.authorizeHttpRequests(request -> {
7272
request
73-
// 권한 없이 요청할 수 있는 API
74-
.requestMatchers(
75-
"/api/v1/auth/login",
76-
"/api/v1/auth/register"
77-
// id중복확인 관련 추후에 추가예정 "/api/v1/auth/check-id"
78-
).permitAll()
79-
// Swagger 설정
80-
.requestMatchers(
81-
"/api-docs/**",
82-
"/swagger-ui.html",
83-
"/swagger-ui/**",
84-
"/swagger-resources/**",
85-
"/webjars/**"
86-
).permitAll()
87-
// 뷰 컨트롤러 추가 시 URL 추가
88-
.requestMatchers("/", "/sign-in","/sign-up").permitAll()
89-
.requestMatchers("/js/**", "/css/**").permitAll()
73+
74+
.requestMatchers("/error", "/favicon.ico").permitAll()
75+
.requestMatchers("/api/v1/auth/login", "/api/v1/auth/register").permitAll() //권한 없이도 요청 가능한 API
76+
.requestMatchers("/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/swagger-resources/**", "/webjars/**").permitAll()
77+
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN") // 관리자(ADMIN) 역할을 가진 사용자만 접근 가능
78+
.requestMatchers("/api/v1/posts/**").hasAnyRole("ADMIN", "USER")
79+
.requestMatchers("/api/v1/students/**").hasAnyRole("ADMIN", "USER")
9080
.anyRequest().authenticated();
9181
})
9282
.sessionManagement(session -> {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.demo.controller.api;
2+
3+
import com.demo.domain.Student;
4+
import com.demo.dto.request.PostCreateRequest;
5+
import com.demo.dto.request.PostUpdateRequest;
6+
import com.demo.dto.response.PostResponse;
7+
import com.demo.domain.Post;
8+
import com.demo.service.PostService;
9+
import lombok.RequiredArgsConstructor;
10+
import lombok.extern.slf4j.Slf4j;
11+
import org.springframework.data.domain.Page;
12+
import org.springframework.security.access.prepost.PreAuthorize;
13+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
14+
import org.springframework.security.core.userdetails.UserDetails;
15+
import org.springframework.web.bind.annotation.*;
16+
import org.springframework.data.domain.PageRequest;
17+
import org.springframework.data.domain.Pageable;
18+
19+
import java.util.List;
20+
import java.util.stream.Collectors;
21+
22+
@Slf4j
23+
@RestController
24+
@RequiredArgsConstructor
25+
@RequestMapping("/api/v1/posts")
26+
public class PostApiController {
27+
28+
private final PostService postService;
29+
30+
@PostMapping
31+
@PreAuthorize("hasRole('USER')")
32+
public PostResponse createPost(@RequestBody PostCreateRequest request, @AuthenticationPrincipal UserDetails userDetails) {
33+
String studentId = userDetails.getUsername();
34+
log.info("Creating post: Student ID: {}, Post Title: {}", studentId, request.getTitle());
35+
PostResponse response = postService.createPost(request, studentId);
36+
log.info("Post created successfully: Post ID: {}", response.getId());
37+
return response;
38+
}
39+
40+
@GetMapping("/{postId}")
41+
public PostResponse getPost(@PathVariable Long postId) {
42+
log.info("Fetching post with ID: {}", postId);
43+
PostResponse response = postService.getPost(postId);
44+
log.info("Post fetched successfully: Post ID: {}", postId);
45+
return response;
46+
}
47+
48+
@PutMapping("/{postId}")
49+
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
50+
public PostResponse updatePost(@PathVariable Long postId, @RequestBody PostUpdateRequest request, @AuthenticationPrincipal UserDetails userDetails) {
51+
String studentId = userDetails.getUsername();
52+
log.info("Updating post: Post ID: {}, Student ID: {}, New Title: {} ", postId, studentId, request.getTitle());
53+
PostResponse response = postService.updatePost(postId, request, studentId);
54+
log.info("Post updated successfully: Post ID: {}", postId);
55+
return response;
56+
}
57+
58+
@DeleteMapping("/{postId}")
59+
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
60+
public void deletePost(@PathVariable Long postId, @AuthenticationPrincipal UserDetails userDetails) {
61+
String studentId = userDetails.getUsername();
62+
log.info("Deleting post: Post ID: {}, Student ID: {}", postId, studentId);
63+
postService.deletePost(postId, studentId);
64+
log.info("Post deleted successfully: Post ID: {}, ", postId);
65+
}
66+
67+
@GetMapping
68+
public Page<PostResponse> getPostList(@RequestParam(required = false) String titleSearch,
69+
@RequestParam(defaultValue = "0") int page,
70+
@RequestParam(defaultValue = "20") int size) {
71+
Pageable pageable = PageRequest.of(page, size); // 페이지 정보 설정
72+
log.info("Fetching post list: Search Query: {}, Page: {}, Size: {}", titleSearch, page, size);
73+
74+
Page<PostResponse> postPage = (titleSearch == null)
75+
? postService.getAllPosts(pageable)
76+
: postService.searchPosts(titleSearch, pageable);
77+
78+
log.info("Fetched {} posts.", postPage.getTotalElements());
79+
return postPage;
80+
}
81+
}

src/main/java/com/demo/domain/Authority.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.demo.domain;
22

3-
import jakarta.persistence.Column;
4-
import jakarta.persistence.Entity;
5-
import jakarta.persistence.Id;
6-
import jakarta.persistence.Table;
3+
import jakarta.persistence.*;
74
import lombok.*;
85

96
/**
@@ -18,10 +15,36 @@
1815
@Entity
1916
@Table(name = "authority")
2017
@Getter
21-
@AllArgsConstructor(access = AccessLevel.PRIVATE)
2218
@NoArgsConstructor(access = AccessLevel.PROTECTED)
2319
public class Authority {
20+
2421
@Id
2522
@Column(name = "authority_name", length = 50)
26-
private String authorityName;
23+
@Enumerated(EnumType.STRING) // Enum 값을 DB에 저장할 때 문자열로 저장하게 했습니다
24+
private Role role;
25+
26+
@Override
27+
public boolean equals(Object o) {
28+
if (this == o) return true;
29+
if (o == null || getClass() != o.getClass()) return false;
30+
Authority authority = (Authority) o;
31+
return role == authority.role;
32+
}
33+
34+
public Authority(Role role) {
35+
this.role = role;
36+
}
37+
38+
public static Authority createRole(Role role) {
39+
return new Authority(role);
40+
}
41+
42+
public String getAuthorityName() {
43+
return role.name(); // Role enum 값을 문자열로 반환
44+
}
45+
46+
@Override
47+
public int hashCode() {
48+
return role.hashCode();
49+
}
2750
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.demo.domain;
2+
3+
import com.demo.dto.request.PostUpdateRequest;
4+
import com.demo.dto.response.PostResponse;
5+
import jakarta.persistence.*;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.AllArgsConstructor;
9+
10+
11+
import java.time.ZoneOffset;
12+
import java.time.LocalDateTime;
13+
14+
@Entity
15+
@Getter
16+
@NoArgsConstructor
17+
@AllArgsConstructor
18+
public class Post {
19+
20+
@Id
21+
@GeneratedValue(strategy = GenerationType.IDENTITY)
22+
private Long id;
23+
private String title;
24+
private String content;
25+
private String description;
26+
27+
@ManyToOne
28+
@JoinColumn(name = "student_id")
29+
private Student student;
30+
31+
private LocalDateTime createdAt;
32+
private LocalDateTime updatedAt;
33+
34+
35+
@PrePersist
36+
public void prePersist() {
37+
this.createdAt = LocalDateTime.now(ZoneOffset.UTC);
38+
this.updatedAt = LocalDateTime.now(ZoneOffset.UTC);
39+
}
40+
41+
// 게시글 수정 시 수정 시간 업데이트
42+
@PreUpdate
43+
public void preUpdate() {
44+
this.updatedAt = LocalDateTime.now(ZoneOffset.UTC);
45+
}
46+
47+
public void update(PostUpdateRequest request) {
48+
this.title = request.getTitle();
49+
this.content = request.getContent();
50+
this.description = request.getDescription();
51+
}
52+
53+
public Post(String title, String content, String description, Student student) {
54+
this.title = title;
55+
this.content = content;
56+
this.description = description;
57+
this.student = student;
58+
}
59+
60+
public PostResponse toResponse() {
61+
return PostResponse.fromPost(this);
62+
}
63+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.demo.domain;
2+
3+
public enum Role {
4+
ROLE_USER("ROLE_USER"),
5+
ROLE_ADMIN("ROLE_ADMIN");
6+
7+
private final String roleName;
8+
9+
Role(String roleName) {
10+
this.roleName = roleName;
11+
}
12+
13+
public String getRoleName() {
14+
return roleName;
15+
}
16+
17+
public static Role fromString(String roleName) {
18+
for (Role role : Role.values()) {
19+
if (role.getRoleName().equals(roleName)) {
20+
return role;
21+
}
22+
}
23+
throw new IllegalArgumentException("Unknown role: " + roleName);
24+
}
25+
}

src/main/java/com/demo/domain/Student.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ public class Student {
4747
)
4848
private Set<Authority> authorities = new HashSet<>();
4949

50+
public void addAuthority(Authority authority) {
51+
this.authorities.add(authority);
52+
}
53+
54+
public boolean hasRole(Role role) {
55+
return this.getAuthorities().stream()
56+
.noneMatch(authority -> authority.getRole().equals(role));
57+
}
58+
5059
@Builder(access = AccessLevel.PRIVATE)
5160
public Student(String name, Long studentNumber, String phoneNumber, String loginId, String password, String email) {
5261
this.name = name;
@@ -87,8 +96,4 @@ public StudentResponse toResponse() {
8796
.password(this.password)
8897
.build();
8998
}
90-
91-
public void addAuthority(Authority authority) {
92-
this.authorities.add(authority);
93-
}
9499
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.demo.dto.request;
2+
3+
import lombok.Getter;
4+
import lombok.Setter;
5+
6+
@Getter
7+
@Setter
8+
public class PostCreateRequest {
9+
10+
private String title;
11+
private String content;
12+
private String description;
13+
private String studentId;
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.demo.dto.request;
2+
3+
import lombok.Getter;
4+
import lombok.Setter;
5+
6+
@Getter
7+
@Setter
8+
public class PostUpdateRequest {
9+
10+
private String title;
11+
private String content;
12+
private String description;
13+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.demo.dto.response;
2+
3+
import lombok.Getter;
4+
import lombok.AllArgsConstructor;
5+
6+
@Getter
7+
@AllArgsConstructor
8+
public class PostResponse {
9+
10+
private Long id; // 게시글 ID
11+
private String title; // 게시글 제목
12+
private String content; // 게시글 내용
13+
private String description; // 게시글 설명
14+
private String createdAt; // 게시글 작성 시간
15+
private String updatedAt; // 게시글 수정 시간
16+
17+
public static PostResponse fromPost(com.demo.domain.Post post) {
18+
return new PostResponse(
19+
post.getId(),
20+
post.getTitle(),
21+
post.getContent(),
22+
post.getDescription(),
23+
post.getCreatedAt() != null ? post.getCreatedAt().toString() : null,
24+
post.getUpdatedAt() != null ? post.getUpdatedAt().toString() : null
25+
);
26+
}
27+
}

0 commit comments

Comments
 (0)