Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#20 채팅 Rest API 추가 #22

Merged
merged 10 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ dependencies {
testImplementation 'org.testcontainers:kafka:1.19.0'
// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
//jackson
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.1'

implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.0.1")
implementation 'io.awspring.cloud:spring-cloud-aws-starter-sqs:3.2.0-M1'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.acc.somsomparty.domain.Festival.controller;

import com.acc.somsomparty.domain.Festival.converter.FestivalConverter;
import com.acc.somsomparty.domain.Festival.dto.FestivalRequestDTO;
import com.acc.somsomparty.domain.Festival.dto.FestivalResponseDTO;
import com.acc.somsomparty.domain.Festival.entity.Festival;

Expand All @@ -18,6 +19,13 @@
public class FestivalController {
private final FestivalQueryService festivalQueryService;

@PostMapping("/create")
@Operation(summary = "축제 생성", description = "축제를 생성합니다.")
public ResponseEntity<Festival> createFestival(FestivalRequestDTO festivalRequestDTO) {
Festival festival = festivalQueryService.save(festivalRequestDTO);
return new ResponseEntity<>(festival, HttpStatus.CREATED);
}

@GetMapping("")
public ResponseEntity<FestivalResponseDTO.FestivalPreViewListDTO> getFestivalList(@RequestParam(defaultValue = "0") Long lastId, @RequestParam(defaultValue = "10") int limit) {
FestivalResponseDTO.FestivalPreViewListDTO festivalPage = festivalQueryService.getFestivalList(lastId, limit);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.acc.somsomparty.domain.Festival.dto;

import java.time.LocalDate;
import lombok.Data;

@Data
public class FestivalRequestDTO {
private String title;
private String description;
private LocalDate startDate;
private LocalDate endDate;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.acc.somsomparty.domain.Festival.entity;

import com.acc.somsomparty.domain.Reservation.entity.Reservation;
import com.acc.somsomparty.domain.Ticket.entity.Ticket;
import com.acc.somsomparty.domain.chatting.entity.ChatRoom;
import com.acc.somsomparty.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
Expand Down Expand Up @@ -44,10 +46,24 @@ public class Festival extends BaseEntity {
@OneToMany(mappedBy = "festival", cascade = CascadeType.ALL)
private List<Ticket> ticketList = new ArrayList<>();

@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "id")
private ChatRoom chatRoom;

public void addChatRoom(ChatRoom chatRoom) {
this.chatRoom = chatRoom;
}
@PrePersist
@PreUpdate
private void setLowercaseValues() {
this.nameLower = this.name.toLowerCase();
this.descriptionLower = this.description.toLowerCase();
}

public Festival(String name, String description, LocalDate startDate, LocalDate endDate) {
this.name = name;
this.description = description;
this.startDate = startDate;
this.endDate = endDate;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.acc.somsomparty.domain.Festival.service;


import com.acc.somsomparty.domain.Festival.dto.FestivalRequestDTO;
import com.acc.somsomparty.domain.Festival.dto.FestivalResponseDTO;
import com.acc.somsomparty.domain.Festival.entity.Festival;

public interface FestivalQueryService {
FestivalResponseDTO.FestivalPreViewListDTO getFestivalList(Long lastId, int offset);
Festival getFestival(Long festivalId);
FestivalResponseDTO.FestivalPreViewListDTO searchFestival(Long lastId, int limit, String keyword);

Festival save(FestivalRequestDTO festivalRequestDTO);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.acc.somsomparty.domain.Festival.service;

import com.acc.somsomparty.domain.Festival.converter.FestivalConverter;
import com.acc.somsomparty.domain.Festival.dto.FestivalRequestDTO;
import com.acc.somsomparty.domain.Festival.dto.FestivalResponseDTO;
import com.acc.somsomparty.domain.Festival.entity.Festival;
import com.acc.somsomparty.domain.Festival.repository.FestivalRepository;
import com.acc.somsomparty.domain.chatting.service.ChattingService;
import com.acc.somsomparty.global.exception.CustomException;
import com.acc.somsomparty.global.exception.error.ErrorCode;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -20,11 +22,12 @@

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Transactional
public class FestivalQueryServiceImpl implements FestivalQueryService {
private final FestivalRepository festivalRepository;
private final RedisTemplate<String, Object> redisTemplate;
private final ObjectMapper objectMapper;
private final ChattingService chattingService;

@Override
public FestivalResponseDTO.FestivalPreViewListDTO getFestivalList(Long lastId, int limit) {
Expand Down Expand Up @@ -68,6 +71,15 @@ public FestivalResponseDTO.FestivalPreViewListDTO searchFestival(Long lastId, in
return responseDTO;
}

@Override
public Festival save(FestivalRequestDTO festivalRequestDTO) {
Festival festival = new Festival(festivalRequestDTO.getTitle(),festivalRequestDTO.getDescription(),
festivalRequestDTO.getStartDate(), festivalRequestDTO.getEndDate());
Festival savedFestival = festivalRepository.save(festival);
chattingService.publishCreateChatRoom(savedFestival);
return savedFestival;
}

private FestivalResponseDTO.FestivalPreViewListDTO generateFestivalPreviewListDTO(List<Festival> festivals, int limit) {
boolean hasNext = festivals.size() > limit;
Long lastId = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.acc.somsomparty.domain.Reservation.entity.Reservation;
import com.acc.somsomparty.domain.User.enums.Role;
import com.acc.somsomparty.domain.chatting.entity.UserChatRoom;
import com.acc.somsomparty.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
Expand Down Expand Up @@ -29,4 +30,6 @@ public class User extends BaseEntity {
private Role role;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Reservation> reservationList = new ArrayList<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserChatRoom> userChatRooms = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.acc.somsomparty.domain.chatting.config;

import java.net.URI;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
Expand All @@ -15,6 +16,7 @@ public DynamoDbClient dynamoDbClient() {
return DynamoDbClient.builder()
.region(Region.AP_NORTHEAST_2)
// 환경 변수에 저장된 키를 기반으로 자동으로 생성
.endpointOverride(URI.create("http://localhost:8000"))
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.acc.somsomparty.domain.chatting.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
return mapper;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.acc.somsomparty.domain.chatting.controller;

import com.acc.somsomparty.domain.chatting.dto.MessageDto;
import com.acc.somsomparty.domain.chatting.dto.MessageListResponse;
import com.acc.somsomparty.domain.chatting.entity.Message;
import com.acc.somsomparty.domain.chatting.dto.UserChatRoomListDto;
import com.acc.somsomparty.domain.chatting.service.ChattingService;
import com.acc.somsomparty.domain.chatting.service.KafkaProducerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
Expand All @@ -14,9 +12,10 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -27,23 +26,17 @@
@Slf4j
@RequestMapping("/festivals/chatting")
public class ChattingController {
private final KafkaProducerService kafkaProducerService;
private final ChattingService chattingService;

@MessageMapping("/chat.send")
public void sendMessage(MessageDto message) {
kafkaProducerService.sendMessage("chat-topic", "chatRoomId"+message.chatRoomId().toString(), message);
}


@Operation(
summary = "채팅방 진입 API",
description = "채팅방에 진입하면 해당 채팅방의 메세지 내역을 페이징해 조회한다."
)
@Parameters({
@Parameter(name = "chatRoomId", description = "채팅방 Id"),
@Parameter(name = "lastEvaluatedSendTime", description = "ddb의 마지막 조회 키(sendTime)"),
@Parameter(name = "limit", description = "페이지당 컨텐츠 개수")
@Parameter(name = "limit", description = "페이지당 컨텐츠 개수"),
@Parameter(name = "userId", description = "입장하는 회원 Id")
})
@GetMapping("/{chatRoomId}")
public ResponseEntity<MessageListResponse> getMessages(
Expand All @@ -53,4 +46,34 @@ public ResponseEntity<MessageListResponse> getMessages(
MessageListResponse messages = chattingService.getMessages(chatRoomId, lastEvaluatedSendTime, limit);
return ResponseEntity.ok(messages);
}

@Operation(
summary = "채팅방 참여 API",
description = "회원이 채팅방에 처음 참여하는 경우 참여 채팅방 목록에 추가한다."
)
@PostMapping("/{chatRoomId}/join")
public ResponseEntity<Long> joinChatRoom(
@PathVariable Long chatRoomId,
@RequestParam Long userId) {
return new ResponseEntity<>(chattingService.joinChatRoom(userId,chatRoomId), HttpStatus.OK);
}

@Operation(
summary = "참여중인 채팅방 목록 API",
description = "회원이 참여 중인 채팅방 목록을 보내준다."
)
@GetMapping("/list/{userId}")
public ResponseEntity<List<UserChatRoomListDto>> getChatRoomList(@PathVariable Long userId){
return ResponseEntity.ok(chattingService.getUserChatRoomList(userId));
}

@Operation(
summary = "채팅방 나가기 API",
description = "회원이 참여중인 채팅방을 나가기"
)
@DeleteMapping("/delete/{userId}")
public ResponseEntity<Void> leaveChatRoom(@PathVariable Long userId, @RequestParam Long chatRoomId){
chattingService.deleteUserChatRoom(userId, chatRoomId);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.acc.somsomparty.domain.chatting.controller;

import com.acc.somsomparty.domain.chatting.dto.MessageDto;
import com.acc.somsomparty.domain.chatting.service.KafkaProducerService;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;

@Controller
@RequiredArgsConstructor
public class WebSocketController {
private final KafkaProducerService kafkaProducerService;

@MessageMapping("/chat.send")
public void sendMessage(MessageDto message) {
kafkaProducerService.sendMessage("chat-topic", "chatRoomId"+message.chatRoomId().toString(), message);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package com.acc.somsomparty.domain.chatting.dto;


import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import lombok.Builder;

@Builder
public record MessageDto(
@NotNull
Long chatRoomId,
Long senderId,
String senderName,
@NotNull
LocalDateTime createdAt,
String content
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.acc.somsomparty.domain.chatting.dto;

import lombok.Builder;

@Builder
public record UserChatRoomListDto(
Long id,
String title,
Long userCount
) {
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.acc.somsomparty.domain.chatting.entity;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -18,12 +22,14 @@ public class ChatRoom {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long festivalId;
private String name;
private Long festivalId;
@OneToMany(mappedBy = "chatRoom", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserChatRoom> userChatRooms = new ArrayList<>();

public ChatRoom(Long festivalId, String festivalName) {
public ChatRoom(String name,Long festivalId) {
this.name = name;
this.festivalId = festivalId;
this.name = festivalName;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.acc.somsomparty.domain.chatting.entity;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import lombok.Setter;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -12,6 +11,7 @@


@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
Expand All @@ -22,7 +22,7 @@ public class Message {
private Long senderId; // 보낸 사람 ID
private String senderName; // 보낸 사람 이름
private Long chatRoomId; // 채팅방 ID
private LocalDateTime sendTime;
private Long sendTime;

@DynamoDbPartitionKey
public Long getChatRoomId() {
Expand All @@ -31,7 +31,7 @@ public Long getChatRoomId() {

@DynamoDbSortKey
public Long getSendTime() {
return sendTime.toEpochSecond(ZoneOffset.UTC);
return sendTime;
}

}
Loading