Skip to content
Open
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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ FROM bellsoft/liberica-openjdk-alpine:17
# FROM openjdk:11-jdk-alpine

CMD ["./gradlew", "clean", "build"]

# or Maven
# CMD ["./mvnw", "clean", "package"]

Expand Down
9 changes: 7 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ services:
container_name: mysql_db
image: mysql:latest
ports:
- "3306:3306"
- "3307:3306"
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
Expand All @@ -46,7 +46,12 @@ services:
image: redis:latest
ports:
- "6379:6379"
command: ["redis-server", "--appendonly", "yes"]
command: [
"redis-server",
"--maxmemory", "1mb",
"--maxmemory-policy", "allkeys-lfu",
"--appendonly", "yes"
]
networks:
- mynetwork

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.jett.domain.chat.dto.ChatMessageDto;
import com.jett.domain.chat.dto.ChatRoomDto;
import com.jett.domain.chat.dto.ChatRoomInfoDto;
import com.jett.domain.chat.service.ChatService;
import com.jett.domain.chat.service.ChatServiceImpl;
import com.jett.global.common.CustomApiResponse;
import com.jett.global.config.security.CustomUserDetails;
Expand All @@ -21,35 +22,35 @@
@RestController
@AllArgsConstructor
public class ChatController {
private final ChatServiceImpl chatServiceImpl;
private final ChatService chatService;
private final SimpMessagingTemplate template;

@Operation(summary = "채팅방 생성", description = "채팅방 생성하기")
@PostMapping("/chat/createRoom")
public ResponseEntity<CustomApiResponse<Long>> createChatRoom(@AuthenticationPrincipal CustomUserDetails customUserDetails, @RequestBody ChatRoomDto chatRoomDto) {
Long userId = customUserDetails.getId();
long roomId = chatServiceImpl.createRoom(userId, chatRoomDto);
long roomId = chatService.createRoom(userId, chatRoomDto);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(roomId));
}

@Operation(summary = "메시지 전송", description = "메시지 전송하기")
@MessageMapping("/sendMessage")
public void sendMessage(@Payload ChatMessageDto chatMessageDto) {
chatServiceImpl.saveMessage(chatMessageDto);
chatService.saveMessage(chatMessageDto);
template.convertAndSend("/sub/chat/room/" + chatMessageDto.getRoomId(), chatMessageDto.getMessage());
}

@Operation(summary = "채팅방 불러오기", description = "채팅방 불러오기")
@GetMapping("/chat/info/{chatroomId}")
public ResponseEntity<CustomApiResponse<ChatRoomInfoDto>> getChatroom(@PathVariable("chatroomId") Long chatroomId) {
ChatRoomInfoDto chatRoomInfoDto = chatServiceImpl.getChatroom(chatroomId);
ChatRoomInfoDto chatRoomInfoDto = chatService.getChatroom(chatroomId);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(chatRoomInfoDto));
}

@Operation(summary = "채팅 내용 불러오기", description = "특정 채팅방에서 채팅 내용 불러오기")
@GetMapping("/chat/{chatroomId}/getMessages")
public ResponseEntity<CustomApiResponse<List<ChatMessageDto>>> getMessage(@PathVariable("chatroomId") Long chatroomId) {
List<ChatMessageDto> messages = chatServiceImpl.getMessages(chatroomId);
List<ChatMessageDto> messages = chatService.getMessages(chatroomId);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(messages));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.jett.domain.invitation.component;

import com.jett.domain.travel.dto.response.PopularPlaceResponse;
import jakarta.transaction.Transactional;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
Expand Down Expand Up @@ -65,4 +67,12 @@ public static Duration toTomorrow() {
final LocalDateTime tomorrow = now.plusDays(1);
return Duration.between(now, tomorrow);
}
@SuppressWarnings("unchecked")
public List<PopularPlaceResponse> getPopularPlaces(String key) {
return (List<PopularPlaceResponse>) redisTemplate.opsForValue().get(key);
}

public void setPopularPlaces(String key, List<PopularPlaceResponse> data, Duration ttl) {
redisTemplate.opsForValue().set(key, data, ttl);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jett.domain.member.controller;

import com.jett.domain.member.service.MemberService;
import com.jett.global.config.security.CustomUserDetails;
import com.jett.global.common.CustomApiResponse;
import com.jett.domain.member.service.MemberServiceImpl;
Expand All @@ -20,41 +21,41 @@
@RestController
@AllArgsConstructor
public class MemberController {
private final MemberServiceImpl memberServiceImpl;
private final MemberService memberService;

@Operation(summary = "회원 가입", description = "새로운 회원을 등록합니다.")
@PostMapping("/signUp")
public ResponseEntity<CustomApiResponse<Long>> signUp(@RequestBody MemberInfoRequestDto memberInfoRequestDto) {
Long memberId = memberServiceImpl.signUp(memberInfoRequestDto);
Long memberId = memberService.signUp(memberInfoRequestDto);
return ResponseEntity.status(HttpStatus.CREATED).body(CustomApiResponse.onSuccess(memberId));
}

@Operation(summary = "로그인", description = "회원 로그인을 처리합니다.")
@PostMapping("/login")
public ResponseEntity<CustomApiResponse<TokenResponseDto>> login(@RequestBody LoginRequestDto loginRequestDto) {
TokenResponseDto tokenResponseDto = memberServiceImpl.login(loginRequestDto);
memberServiceImpl.updateLastLoginDate(loginRequestDto.getEmail());
TokenResponseDto tokenResponseDto = memberService.login(loginRequestDto);
memberService.updateLastLoginDate(loginRequestDto.getEmail());
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(tokenResponseDto));
}

@Operation(summary = "카카오 로그인", description = "카카오 로그인을 처리합니다.")
@GetMapping("/kakao")
public RedirectView kakaoConnect() {
String url = memberServiceImpl.kakaoConnect();
String url = memberService.kakaoConnect();
return new RedirectView(url);
}

@GetMapping("/kakao/callback")
public ResponseEntity<CustomApiResponse<TokenResponseDto>> kakaoLogin(@RequestParam("code") String code) {
TokenResponseDto tokenResponseDto = memberServiceImpl.getKakaoToken(code);
TokenResponseDto tokenResponseDto = memberService.getKakaoToken(code);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(tokenResponseDto));
}

@Operation(summary = "사용자 정보 가져오기", description = "사용자 정보를 가져옵니다.")
@GetMapping("/getInfo")
public ResponseEntity<CustomApiResponse<MemberDto>> getMember(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
Long userId=customUserDetails.getId();
MemberDto memberDto = memberServiceImpl.getMember(userId);
MemberDto memberDto = memberService.getMember(userId);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(memberDto));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jett.domain.schedule.controller;

import com.jett.domain.schedule.service.ScheduleService;
import com.jett.global.common.CustomApiResponse;
import com.jett.global.config.security.CustomUserDetails;
import com.jett.domain.schedule.dto.ScheduleRequest;
Expand All @@ -21,28 +22,28 @@
@AllArgsConstructor

public class ScheduleController {
private final ScheduleServiceImpl scheduleServiceImpl;
private final ScheduleService scheduleService;
@Operation(summary = "여행 일정 추가", description = "여행 일정 추가")
@PostMapping("/{travelId}/add")
public ResponseEntity<CustomApiResponse<List<Long>>> addSchedule(@AuthenticationPrincipal CustomUserDetails customUserDetails, @PathVariable Long travelId , @RequestBody List<ScheduleRequest> scheduleRequests) {
Long userId=customUserDetails.getId();
List<Long> scheduleId= scheduleServiceImpl.addSchedule(userId,travelId, scheduleRequests);
List<Long> scheduleId= scheduleService.addSchedule(userId,travelId, scheduleRequests);
return ResponseEntity.status(HttpStatus.CREATED).body(CustomApiResponse.onSuccess(scheduleId));
}


@Operation(summary = "일정 조회", description = "선택 여행 일정 조회")
@GetMapping("/lists/{travelId}")
public ResponseEntity<CustomApiResponse<List<ScheduleResponse>>> getSchedule(@PathVariable Long travelId) {
List<ScheduleResponse> scheduleResponses = scheduleServiceImpl.getAllSchedule(travelId);
List<ScheduleResponse> scheduleResponses = scheduleService.getAllSchedule(travelId);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(scheduleResponses));

}
@Operation(summary = "일정 삭제", description = "스케줄 일정 선택 여행 삭제")
@DeleteMapping("/{travelId}/{scheduleId}")
public ResponseEntity<CustomApiResponse<String>> deleteSchedule(@AuthenticationPrincipal CustomUserDetails customUserDetails,@PathVariable Long travelId,@PathVariable Long scheduleId) {
Long userId=customUserDetails.getId();
scheduleServiceImpl.deleteSchedule(userId,travelId,scheduleId);
scheduleService.deleteSchedule(userId,travelId,scheduleId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(CustomApiResponse.onSuccess("일정 삭제됌"));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package com.jett.domain.travel.controller;

import com.jett.domain.invitation.component.RedisService;
import com.jett.domain.travel.dto.request.TravelInviteRequest;
import com.jett.domain.travel.dto.request.TravelRequest;
import com.jett.domain.travel.dto.response.PopularPlaceResponse;
import com.jett.domain.travel.dto.response.TravelResponse;
import com.jett.domain.travel.service.TravelService;
import com.jett.global.common.CustomApiResponse;
import com.jett.global.config.security.CustomUserDetails;
import com.jett.domain.travel.kakao.KakaoMapService;
import com.jett.domain.travel.opendata.TourApiService;
import com.jett.domain.travel.service.TravelServiceImpl;
import com.jett.domain.travel.opendata.service.TourApiService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import lombok.AllArgsConstructor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
Expand All @@ -29,12 +34,14 @@
@RequestMapping("/travel")
@RestController
@AllArgsConstructor
@Slf4j

public class TravelController {

private final KakaoMapService kakaoMapService;
private final TravelServiceImpl travelServiceImpl;
private final TravelService travelService;
private final TourApiService tourApiService;
private final RedisService redisService;

@Operation(summary = "장소 검색", description = "키워드를 통해 장소 검색하기")
@GetMapping("/kakao/searchKeyword")
Expand All @@ -50,15 +57,15 @@ public ResponseEntity<CustomApiResponse<String>> searchKeyword(
public ResponseEntity<CustomApiResponse<Long>> addTravel(@RequestBody TravelRequest travelRequest,
@AuthenticationPrincipal CustomUserDetails customUserDetails) {
Long userId = customUserDetails.getId();
Long travelId = travelServiceImpl.addTravel(userId, travelRequest);
Long travelId = travelService.addTravel(userId, travelRequest);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess(travelId));
}

@Operation(summary = "여행에 친구 초대", description = " 여행에 친구들 초대하기")
@PostMapping("/invite/{travelId}")
public ResponseEntity<CustomApiResponse<String>> inviteTravel(
@RequestBody TravelInviteRequest travelInviteRequest, @PathVariable Long travelId) {
travelServiceImpl.inviteTravel(travelInviteRequest, travelId);
travelService.inviteTravel(travelInviteRequest, travelId);
return ResponseEntity.status(HttpStatus.OK).body(CustomApiResponse.onSuccess("친구 초대됌"));
}

Expand All @@ -67,7 +74,7 @@ public ResponseEntity<CustomApiResponse<String>> inviteTravel(
public ResponseEntity<CustomApiResponse<List<TravelResponse>>> checkTravelSchedule(
@AuthenticationPrincipal CustomUserDetails customUserDetails) {
Long userId = customUserDetails.getId();
List<TravelResponse> checkTravelResult = travelServiceImpl.getAllTravel(userId);
List<TravelResponse> checkTravelResult = travelService.getAllTravel(userId);
return ResponseEntity.status(HttpStatus.OK)
.body(CustomApiResponse.onSuccess(checkTravelResult));
}
Expand All @@ -77,7 +84,7 @@ public ResponseEntity<CustomApiResponse<List<TravelResponse>>> checkTravelSchedu
public ResponseEntity<CustomApiResponse<TravelResponse>> checkOnlyTravelSchedule(
@AuthenticationPrincipal CustomUserDetails customUserDetails, @PathVariable Long travelId) {
Long userId = customUserDetails.getId();
TravelResponse checkTravelResult = travelServiceImpl.checkOnlyTravelSchedule(userId,travelId);
TravelResponse checkTravelResult = travelService.checkOnlyTravelSchedule(userId,travelId);
return ResponseEntity.status(HttpStatus.OK)
.body(CustomApiResponse.onSuccess(checkTravelResult));
}
Expand All @@ -87,13 +94,48 @@ public ResponseEntity<CustomApiResponse<TravelResponse>> checkOnlyTravelSchedule
public ResponseEntity<CustomApiResponse<String>> deleteTravel(
@AuthenticationPrincipal CustomUserDetails customUserDetails, @PathVariable Long travelId) {
Long userId = customUserDetails.getId();
travelServiceImpl.deleteTravel(userId, travelId);
travelService.deleteTravel(userId, travelId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(CustomApiResponse.onSuccess("여행 삭제됌"));

}

@Operation(summary = "지역별 인기여행지 조회", description = "지역을 입력받을 후 해당하는 위치 관광지 조회")
@Operation(summary = "지역별 인기여행지 조회", description = "지역을 MySQL 또는 Redis에서 조회")
@GetMapping("/popularLists")
public ResponseEntity<CustomApiResponse<List<PopularPlaceResponse>>> getPopularPlaceList(
@RequestParam String place) {
Instant startTime = Instant.now();
String redisKey = "지역명"+ place;
List<PopularPlaceResponse> cachedData = redisService.getPopularPlaces(redisKey);
if (cachedData != null) {
log.info("캐시데이터 저장되어있었음");
Instant endTime = Instant.now();
long duration = java.time.Duration.between(startTime, endTime).toNanos();
log.info("API 호출 시간 (캐시 데이터 조회): " + duration + "ns");
return ResponseEntity.ok(CustomApiResponse.onSuccess(cachedData));
}// 레디스에 저장되어있음
log.info("레디스에 없었음");

// 없으면 DB
// DB 조회 시작 시간 기록
Instant dbStartTime = Instant.now();
List<PopularPlaceResponse> popularResults = tourApiService.getPopularPlaceList(place);
// DB 조회 끝나고 시간 측정
Instant dbEndTime = Instant.now();
long dbDuration = java.time.Duration.between(dbStartTime, dbEndTime).toNanos();
log.info("API 호출 시간 (DB 조회): " + dbDuration + "ns");
redisService.setPopularPlaces(redisKey, popularResults, Duration.ofMinutes(30));

// 끝 시간 기록 및 소요 시간 계산
Instant endTime = Instant.now();
long duration = java.time.Duration.between(startTime, endTime).toNanos();
log.info("API 호출 시간 (DB 조회 및 캐시 저장): " + duration + "ns");
return ResponseEntity.ok(CustomApiResponse.onSuccess(popularResults));
}




@Operation(summary = "API 요청 테스트 DB테스트 및 저장용지역별 인기여행지 조회 사용X", description = "지역을 입력받을 후 해당하는 위치 관광지 조회")
@GetMapping("/popularLists/Test")
public ResponseEntity<CustomApiResponse<List<PopularPlaceResponse>>> getPopularPlace(
@RequestParam String place) {
try {
Expand All @@ -104,5 +146,12 @@ public ResponseEntity<CustomApiResponse<List<PopularPlaceResponse>>> getPopularP
.body(CustomApiResponse.onFailure(e.getMessage(), null));
}
}
@Operation(summary = "지역별 인기여행지 MYSQL 대량 저장 사용 X", description = "모든 지역 DB 저장 API")
@GetMapping("/AllLists")
public ResponseEntity<CustomApiResponse<String>> saveAllPopularPlace() {
tourApiService.saveAllPopularPlace();
return ResponseEntity.status(HttpStatus.OK)
.body(CustomApiResponse.onSuccess("Save 완료"));
}

}
4 changes: 3 additions & 1 deletion src/main/java/com/jett/domain/travel/entity/Travel.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

Expand Down Expand Up @@ -46,6 +47,7 @@ public class Travel {

private Boolean isDeleted = false;

@OneToMany(mappedBy = "travel")
@OneToMany(mappedBy = "travel", fetch = FetchType.LAZY)
@BatchSize(size = 10)
private List<TravelMember> travelMembers;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.jett.domain.travel.opendata.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "popular_place")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PopularPlace {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String region;

@Column(nullable = false)
private String title;

@Column(nullable = false)
private String address;

@Column(name = "image_url")
private String imageUrl;
}
Loading