Skip to content

Commit bd1e053

Browse files
authored
Merge pull request #4 from eunhye-ahn/feature/group-core-0516
[BE] 그룹 : 생성/조회/참여 API 개발
2 parents 74c13ef + 1d9dfc6 commit bd1e053

36 files changed

+723
-22
lines changed

polling-app-server/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
<artifactId>spring-security-test</artifactId>
8585
<scope>test</scope>
8686
</dependency>
87+
<dependency>
88+
<groupId>org.projectlombok</groupId>
89+
<artifactId>lombok</artifactId>
90+
<scope>provided</scope>
91+
</dependency>
8792
</dependencies>
8893

8994
<build>

polling-app-server/src/main/java/com/example/polls/controller/AuthController.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
import com.example.polls.model.Role;
55
import com.example.polls.model.RoleName;
66
import com.example.polls.model.User;
7-
import com.example.polls.payload.ApiResponse;
8-
import com.example.polls.payload.JwtAuthenticationResponse;
9-
import com.example.polls.payload.LoginRequest;
10-
import com.example.polls.payload.SignUpRequest;
7+
import com.example.polls.payload.Response.ApiResponse;
8+
import com.example.polls.payload.Response.JwtAuthenticationResponse;
9+
import com.example.polls.payload.Request.LoginRequest;
10+
import com.example.polls.payload.Request.SignUpRequest;
1111
import com.example.polls.repository.RoleRepository;
1212
import com.example.polls.repository.UserRepository;
1313
import com.example.polls.security.JwtTokenProvider;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.example.polls.controller;
2+
3+
import com.example.polls.model.User;
4+
import com.example.polls.payload.Request.CreateGroupRequest;
5+
import com.example.polls.payload.Request.JoinGroupRequest;
6+
import com.example.polls.payload.Response.GroupDetailResponse;
7+
import com.example.polls.payload.Response.GroupSummaryResponse;
8+
import com.example.polls.repository.UserRepository;
9+
import com.example.polls.security.UserPrincipal;
10+
import com.example.polls.service.GroupService;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.http.HttpStatus;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
15+
import org.springframework.web.bind.annotation.*;
16+
17+
import java.util.List;
18+
19+
@RestController
20+
@RequestMapping("/api/groups")
21+
@RequiredArgsConstructor
22+
public class GroupController {
23+
private final UserRepository userRepository;
24+
25+
private final GroupService groupService;
26+
27+
//그룹생성
28+
@PostMapping
29+
public ResponseEntity<GroupSummaryResponse> createGroup(
30+
@RequestBody CreateGroupRequest request,
31+
@AuthenticationPrincipal UserPrincipal userPrincipal) {
32+
33+
User creator = userRepository.findById(userPrincipal.getId())
34+
.orElseThrow(()-> new RuntimeException("사용자 없음"));
35+
36+
GroupSummaryResponse response = groupService.createGroup(request, creator);
37+
return ResponseEntity.status(HttpStatus.CREATED).body(response);
38+
}
39+
40+
@GetMapping("/my")
41+
public ResponseEntity<List<GroupSummaryResponse>> getMyGroups(@AuthenticationPrincipal UserPrincipal userPrincipal) {
42+
Long useId = userPrincipal.getId();
43+
List<GroupSummaryResponse> groups = groupService.getGroupsForUser(useId);
44+
return ResponseEntity.ok(groups);
45+
}
46+
47+
@PostMapping("/join")
48+
public ResponseEntity<String> joinGroup(@RequestBody JoinGroupRequest request, @AuthenticationPrincipal UserPrincipal userPrincipal) {
49+
groupService.joinCode(userPrincipal.getId(),request.getJoinCode());
50+
return ResponseEntity.ok("그룹에 성공적으로 참여했습니다.");
51+
}
52+
53+
//그룹 상세조회
54+
@GetMapping("/{groupId}")
55+
public ResponseEntity<GroupDetailResponse> getGroupDetail(@PathVariable Long groupId,
56+
@AuthenticationPrincipal UserPrincipal userPrincipal) {
57+
GroupDetailResponse response = groupService.getGroupDetail(groupId,userPrincipal.getId());
58+
return ResponseEntity.ok(response);
59+
}
60+
61+
@GetMapping("{groupId}/members")
62+
public ResponseEntity<List<GroupDetailResponse.MemberSummary>> getGroupMembers(@PathVariable Long groupId,
63+
@AuthenticationPrincipal UserPrincipal userPrincipal) {
64+
GroupDetailResponse response = groupService.getGroupDetail(groupId, userPrincipal.getId());
65+
return ResponseEntity.ok(response.getMembers());
66+
}
67+
68+
}

polling-app-server/src/main/java/com/example/polls/controller/PollController.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.example.polls.controller;
22

33
import com.example.polls.model.*;
4-
import com.example.polls.payload.*;
4+
import com.example.polls.payload.Request.PollRequest;
5+
import com.example.polls.payload.Request.VoteRequest;
6+
import com.example.polls.payload.Response.ApiResponse;
7+
import com.example.polls.payload.Response.PagedResponse;
8+
import com.example.polls.payload.Response.PollResponse;
59
import com.example.polls.repository.PollRepository;
610
import com.example.polls.repository.UserRepository;
711
import com.example.polls.repository.VoteRepository;

polling-app-server/src/main/java/com/example/polls/controller/UserController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.example.polls.exception.ResourceNotFoundException;
44
import com.example.polls.model.User;
55
import com.example.polls.payload.*;
6+
import com.example.polls.payload.Response.PagedResponse;
7+
import com.example.polls.payload.Response.PollResponse;
68
import com.example.polls.repository.PollRepository;
79
import com.example.polls.repository.UserRepository;
810
import com.example.polls.repository.VoteRepository;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.example.polls.exception;
2+
3+
public class AlreadyJoinedException extends RuntimeException {
4+
public AlreadyJoinedException(String message) {
5+
super(message);
6+
}
7+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.example.polls.exception;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.web.bind.annotation.ExceptionHandler;
6+
import org.springframework.web.bind.annotation.RestControllerAdvice;
7+
8+
import java.util.Map;
9+
10+
@RestControllerAdvice
11+
public class GlobalExceptionHandler {
12+
13+
// 400 Bad Request
14+
@ExceptionHandler(BadRequestException.class)
15+
public ResponseEntity<?> handleBadRequest(BadRequestException ex) {
16+
return buildErrorResponse(HttpStatus.BAD_REQUEST, ex.getMessage());
17+
}
18+
19+
// 404 Not Found
20+
@ExceptionHandler(ResourceNotFoundException.class)
21+
public ResponseEntity<?> handleNotFound(ResourceNotFoundException ex) {
22+
return buildErrorResponse(HttpStatus.NOT_FOUND, ex.getMessage());
23+
}
24+
25+
// 409 Conflict
26+
@ExceptionHandler(AlreadyJoinedException.class)
27+
public ResponseEntity<?> handleConflict(AlreadyJoinedException ex) {
28+
return buildErrorResponse(HttpStatus.CONFLICT, ex.getMessage());
29+
}
30+
31+
// 500 Internal Server Error (예외 누락 시 fallback)
32+
@ExceptionHandler(Exception.class)
33+
public ResponseEntity<?> handleException(Exception ex) {
34+
return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, "서버 오류가 발생했습니다.");
35+
}
36+
37+
// 공통 응답 포맷 생성 메서드
38+
private ResponseEntity<?> buildErrorResponse(HttpStatus status, String message) {
39+
return ResponseEntity.status(status).body(Map.of(
40+
"status", status.value(),
41+
"error", status.getReasonPhrase(),
42+
"message", message
43+
));
44+
}
45+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.example.polls.model;
2+
3+
import com.example.polls.model.audit.UserDateAudit;
4+
5+
import javax.persistence.*;
6+
import javax.validation.constraints.NotBlank;
7+
import javax.validation.constraints.Size;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
@Entity
12+
@Table(name = "`groups`")
13+
public class Group extends UserDateAudit {
14+
@Id
15+
@GeneratedValue(strategy = GenerationType.IDENTITY)
16+
private Long id;
17+
18+
@NotBlank
19+
@Size(max = 100)
20+
private String name;
21+
22+
@Size(max=255)
23+
private String description;
24+
25+
@Column(unique = true)
26+
private String joinCode;
27+
28+
@Column
29+
private String imageUrl;
30+
31+
@OneToMany(mappedBy="group",cascade = CascadeType.ALL, orphanRemoval = true)
32+
private List<GroupMember> members = new ArrayList<>();
33+
34+
@OneToMany(mappedBy = "group", cascade=CascadeType.ALL)
35+
private List<Poll> polls = new ArrayList<>();
36+
37+
38+
public String getImageUrl() {
39+
return imageUrl;
40+
}
41+
42+
public void setImageUrl(String imageUrl) {
43+
this.imageUrl = imageUrl;
44+
}
45+
46+
public Long getId() {
47+
return id;
48+
}
49+
50+
public void setId(Long id) {
51+
this.id = id;
52+
}
53+
54+
public String getName() {
55+
return name;
56+
}
57+
58+
public void setName(String name) {
59+
this.name = name;
60+
}
61+
62+
public String getDescription() {
63+
return description;
64+
}
65+
66+
public void setDescription(String description) {
67+
this.description = description;
68+
}
69+
70+
public String getJoinCode() {
71+
return joinCode;
72+
}
73+
74+
public void setJoinCode(String joinCode) {
75+
this.joinCode = joinCode;
76+
}
77+
78+
public List<GroupMember> getMembers() {
79+
return members;
80+
}
81+
82+
public void setMembers(List<GroupMember> members) {
83+
this.members = members;
84+
}
85+
86+
public List<Poll> getPolls() {
87+
return polls;
88+
}
89+
90+
public void setPolls(List<Poll> polls) {
91+
this.polls = polls;
92+
}
93+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.example.polls.model;
2+
3+
import org.springframework.data.annotation.CreatedDate;
4+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
5+
6+
import javax.persistence.*;
7+
import java.time.Instant;
8+
9+
@Entity
10+
@EntityListeners(AuditingEntityListener.class)
11+
@Table(name = "group_members")
12+
public class GroupMember {
13+
@Id
14+
@GeneratedValue(strategy = GenerationType.IDENTITY)
15+
private Long id;
16+
17+
@ManyToOne(fetch = FetchType.LAZY)
18+
@JoinColumn(name = "group_id", nullable = false)
19+
private Group group;
20+
21+
@ManyToOne(fetch = FetchType.LAZY)
22+
@JoinColumn(name = "user_id", nullable = false)
23+
private User user;
24+
25+
@Enumerated(EnumType.STRING)
26+
@Column(name = "role", nullable = false)
27+
private GroupRole role;
28+
29+
@CreatedDate
30+
@Column(name = "joined_at", nullable = false, updatable = false)
31+
private Instant joinedAt;
32+
33+
public Instant getJoinedAt() {
34+
return joinedAt;
35+
}
36+
37+
public void setJoinedAt(Instant joinedAt) {
38+
this.joinedAt = joinedAt;
39+
}
40+
41+
public GroupMember(Long id, Group group, User user, GroupRole role) {
42+
this.id = id;
43+
this.group = group;
44+
this.user = user;
45+
this.role = role;
46+
}
47+
48+
public GroupMember() {
49+
50+
}
51+
52+
public Long getId() {
53+
return id;
54+
}
55+
56+
public void setId(Long id) {
57+
this.id = id;
58+
}
59+
60+
public Group getGroup() {
61+
return group;
62+
}
63+
64+
public void setGroup(Group group) {
65+
this.group = group;
66+
}
67+
68+
public User getUser() {
69+
return user;
70+
}
71+
72+
public void setUser(User user) {
73+
this.user = user;
74+
}
75+
76+
public GroupRole getRole() {
77+
return role;
78+
}
79+
80+
public void setRole(GroupRole role) {
81+
this.role = role;
82+
}
83+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.example.polls.model;
2+
3+
public enum GroupRole {
4+
LEADER, MEMBER
5+
}

0 commit comments

Comments
 (0)