Skip to content

Commit

Permalink
Merge pull request #240 from haedoang/feature/contact
Browse files Browse the repository at this point in the history
[feature/contact] 알림 기능 추가
  • Loading branch information
haedoang authored Nov 5, 2023
2 parents e75c791 + 0f844bf commit bb9c64c
Show file tree
Hide file tree
Showing 43 changed files with 558 additions and 75 deletions.
5 changes: 2 additions & 3 deletions src/main/java/com/koliving/api/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import static com.koliving.api.token.confirmation.ConfirmationTokenType.SIGN_UP;

import com.koliving.api.base.ErrorResponse;
import com.koliving.api.base.ServiceError;
import com.koliving.api.base.exception.KolivingServiceException;
import com.koliving.api.dto.ConfirmationTokenErrorDto;
import com.koliving.api.dto.ResponseDto;
Expand All @@ -18,8 +17,8 @@
import com.koliving.api.token.confirmation.ConfirmationToken;
import com.koliving.api.token.confirmation.ConfirmationTokenService;
import com.koliving.api.token.confirmation.ConfirmationTokenType;
import com.koliving.api.user.SignUpStatus;
import com.koliving.api.user.User;
import com.koliving.api.user.domain.SignUpStatus;
import com.koliving.api.user.domain.User;
import com.koliving.api.user.application.UserService;
import com.koliving.api.utils.HttpUtils;
import java.util.Locale;
Expand Down
35 changes: 24 additions & 11 deletions src/main/java/com/koliving/api/KolivingApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static com.koliving.api.location.domain.LocationType.GU;

import com.google.common.collect.Sets;
import com.koliving.api.email.IEmailService;
import com.koliving.api.email.MailType;
import com.koliving.api.file.domain.ImageFile;
import com.koliving.api.file.infra.ImageFileRepository;
import com.koliving.api.i18n.Language;
Expand All @@ -21,9 +23,11 @@
import com.koliving.api.room.infra.FurnishingRepository;
import com.koliving.api.room.infra.LikeRepository;
import com.koliving.api.room.infra.RoomRepository;
import com.koliving.api.user.User;
import com.koliving.api.user.UserRepository;
import com.koliving.api.user.UserRole;
import com.koliving.api.user.domain.Notification;
import com.koliving.api.user.domain.User;
import com.koliving.api.user.infra.NotificationRepository;
import com.koliving.api.user.infra.UserRepository;
import com.koliving.api.user.domain.UserRole;
import jakarta.annotation.PostConstruct;
import java.time.LocalDate;
import java.util.Arrays;
Expand All @@ -38,9 +42,6 @@
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@EnableJpaAuditing
@SpringBootApplication
Expand Down Expand Up @@ -69,25 +70,33 @@ CommandLineRunner commandLineRunner(
ImageFileRepository imageFileRepository,
LikeRepository likeRepository,
UserRepository userRepository,
PasswordEncoder encoder
PasswordEncoder encoder,
NotificationRepository notificationRepository
) {
return args -> {
initImageFiles(imageFileRepository);
initFurnishings(furnishingRepository);
initLocations(locationRepository);
initLanguages(languageRepository);
User user = initUser(userRepository, encoder);
User user = initUser(userRepository, encoder, notificationRepository);
initRooms(roomRepository, locationRepository, furnishingRepository, imageFileRepository, likeRepository, user);
};
}

private User initUser(UserRepository userRepository, PasswordEncoder encoder) {
private User initUser(UserRepository userRepository, PasswordEncoder encoder, NotificationRepository notificationRepository) {
final User user = User.valueOf("[email protected]", encoder.encode("test1234!@"), UserRole.USER);
final User user2 = User.valueOf("[email protected]", encoder.encode("test1234!@"), UserRole.USER);
final User manager = User.valueOf("[email protected]", encoder.encode("test1234!@"), UserRole.ADMIN);

userRepository.saveAll(List.of(user2, manager));
return userRepository.save(user);
userRepository.saveAll(List.of(user, manager));
userRepository.save(user2);

// 알림
notificationRepository.save(Notification.of(user, user2));
notificationRepository.save(Notification.of(manager, user));
notificationRepository.save(Notification.of(user2, user));

return user2;
}

private void initImageFiles(ImageFileRepository imageFileRepository) {
Expand Down Expand Up @@ -171,8 +180,12 @@ private void initLanguages(LanguageRepository languageRepository) {
Language.valueOf("ko", "email_duplication", "이미 존재하는 이메일입니다 : {0}"),
Language.valueOf("en", "auth_email_subject", "Confirm your email"),
Language.valueOf("ko", "auth_email_subject", "이메일 인증을 완료하세요"),
Language.valueOf("en", "contact_email_subject", "Someone is interested in your posts."),
Language.valueOf("ko", "contact_email_subject", "누군가 당신의 게시글에 관심이 있어요."),
Language.valueOf("en", "auth_email_subtitle", "Click the link below to proceed with authentication"),
Language.valueOf("ko", "auth_email_subtitle", "아래 링크를 클릭하셔서 인증을 진행하세요"),
Language.valueOf("en", "contact_email_subtitle", "Possible roommate has shown interest in your room!"),
Language.valueOf("ko", "contact_email_subtitle", "룸메이트가 당신의 방에 관심을 보였습니다!"),
Language.valueOf("en", "auth_email_link_guidance",
"If the button doesn't work, copy and paste the link below into url"),
Language.valueOf("ko", "auth_email_link_guidance", "버튼이 동작하지 않다면, 아래 링크를 url에 붙여 인증을 시도하세요"),
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/koliving/api/auth/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
import com.koliving.api.exception.DuplicateResourceException;
import com.koliving.api.exception.NonExistentResourceException;
import com.koliving.api.token.confirmation.ConfirmationTokenType;
import com.koliving.api.user.SignUpStatus;
import com.koliving.api.user.User;
import com.koliving.api.user.UserPropertyEditor;
import com.koliving.api.user.domain.SignUpStatus;
import com.koliving.api.user.domain.User;
import com.koliving.api.user.infra.UserPropertyEditor;
import com.koliving.api.user.application.UserService;
import com.koliving.api.utils.HttpUtils;
import com.koliving.api.validation.EmailDuplicationValidator;
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/com/koliving/api/auth/AuthFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
import com.koliving.api.token.confirmation.ConfirmationToken;
import com.koliving.api.token.confirmation.ConfirmationTokenType;
import com.koliving.api.token.confirmation.IConfirmationTokenService;
import com.koliving.api.user.User;
import com.koliving.api.user.UserRepository;
import com.koliving.api.user.application.UserService;
import com.koliving.api.user.domain.User;
import com.koliving.api.user.infra.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/koliving/api/base/KLDescription.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.koliving.api.base;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

/**
* author : haedoang date : 2023/11/05 description :
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface KLDescription {
String value() default "";

String example() default "";
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.koliving.api.config;

import com.koliving.api.dto.ProfileDto;
import com.koliving.api.user.User;
import com.koliving.api.user.domain.User;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.context.annotation.Bean;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/koliving/api/dto/ProfileDto.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.koliving.api.dto;

import com.koliving.api.user.Gender;
import com.koliving.api.user.domain.Gender;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/com/koliving/api/email/EmailService.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.koliving.api.email;

import com.koliving.api.properties.EmailProperties;
import com.koliving.api.properties.FrontProperties;
import com.koliving.api.room.infra.RoomContactEvent;
import com.koliving.api.user.domain.User;
import com.koliving.api.utils.HttpUtils;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.event.EventListener;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.MailException;
Expand All @@ -33,6 +37,43 @@ public class EmailService implements IEmailService {
private final EmailProperties emailProperties;
private final HttpUtils httpUtils;

@Async("mailExecutor")
@Override
public void sendRoomContact(String to, String contact, String message, User sender, String link) {
try {
Locale currentLocale = httpUtils.getLocaleForLanguage(LocaleContextHolder.getLocale());

Map<String, Object> variables = new HashMap<>();
variables.put("title", "KOLIVING");
variables.put("subtitle", messageSource.getMessage("contact_email_subtitle", null, currentLocale));
variables.put("contact", contact);
variables.put("message", message);
variables.put("roomLink", link);
variables.put("userName", sender.getFullName());
variables.put("userAge", sender.getAge());
variables.put("userImageProfile", sender.getImageProfile());
variables.put("userGender", sender.getGender());
variables.put("userDescription", sender.getDescription());

MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");

helper.setSubject(messageSource.getMessage("contact_email_subject", null, currentLocale));
helper.setTo(to);
helper.setText(emailTemplateUtil.generateEmail(MailType.CONTACT, variables), true);
helper.setFrom(emailProperties.getUsername());
helper.addInline("logo", new ClassPathResource("static/image/logo-black.jpg"));

mailSender.send(mimeMessage);
} catch (MessagingException e) {
log.error("failed to generate email", e);
throw new MailParseException("failed to generate email", e);
} catch (MailException e) {
log.error("failed to send email", e);
throw new MailSendException("Failed to send email", e);
}
}

@Async("mailExecutor")
@Override
public void send(MailType type, String to, String link) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/koliving/api/email/IEmailService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.koliving.api.email;

import com.koliving.api.user.domain.User;

public interface IEmailService {

void send(MailType type, String to, String url);

void sendRoomContact(String to, String contactInfo, String message, User sender, String link);
}
3 changes: 2 additions & 1 deletion src/main/java/com/koliving/api/email/MailType.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.koliving.api.email;

public enum MailType {
AUTH("auth-email-template");
AUTH("auth-email-template"),
CONTACT("contact-email-template");

private final String template;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.koliving.api.my.application.dto;

import com.koliving.api.file.domain.ImageFile;
import com.koliving.api.user.Gender;
import com.koliving.api.user.User;
import com.koliving.api.user.domain.Gender;
import com.koliving.api.user.domain.User;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.lang.Nullable;

Expand Down
25 changes: 24 additions & 1 deletion src/main/java/com/koliving/api/my/ui/MyController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
import com.koliving.api.my.application.dto.UserProfileUpdateRequest;
import com.koliving.api.room.application.RoomService;
import com.koliving.api.room.application.dto.RoomResponse;
import com.koliving.api.user.User;
import com.koliving.api.user.application.dto.NotificationResponse;
import com.koliving.api.user.domain.User;
import com.koliving.api.user.application.UserService;
import com.koliving.api.user.application.dto.UserResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -99,4 +101,25 @@ public ResponseEntity<Page<RoomResponse>> getLikedRooms(Pageable pageable, @Auth
return ResponseEntity.ok()
.body(responses);
}

@Operation(
summary = "알림 조회",
description = "알림 리스트 조회합니다",
responses = {
@ApiResponse(
responseCode = "200",
description = "알림 리스트 조회 성공"
),
@ApiResponse(
responseCode = "400",
description = "알림 리스트 조회 실패",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
),
})
@GetMapping("/notification")
public ResponseEntity<List<NotificationResponse>> getNotifications(@AuthenticationPrincipal User user) {
List<NotificationResponse> responses = userService.getNotifications(user);
return ResponseEntity.ok()
.body(responses);
}
}
26 changes: 24 additions & 2 deletions src/main/java/com/koliving/api/room/application/RoomService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,37 @@

import static com.koliving.api.base.ServiceError.FORBIDDEN;
import static com.koliving.api.base.ServiceError.RECORD_NOT_EXIST;
import static com.koliving.api.base.ServiceError.UNAUTHORIZED;

import com.google.common.collect.Sets;
import com.koliving.api.base.ServiceError;
import com.koliving.api.base.exception.KolivingServiceException;
import com.koliving.api.email.IEmailService;
import com.koliving.api.file.domain.ImageFile;
import com.koliving.api.file.infra.ImageFileRepository;
import com.koliving.api.location.domain.Location;
import com.koliving.api.location.infra.LocationRepository;
import com.koliving.api.properties.FrontProperties;
import com.koliving.api.room.application.dto.RoomContactRequest;
import com.koliving.api.room.application.dto.RoomResponse;
import com.koliving.api.room.application.dto.RoomSaveRequest;
import com.koliving.api.room.application.dto.RoomSearchCondition;
import com.koliving.api.room.domain.Furnishing;
import com.koliving.api.room.domain.Like;
import com.koliving.api.room.domain.Room;
import com.koliving.api.room.infra.RoomContactEvent;
import com.koliving.api.room.infra.FurnishingRepository;
import com.koliving.api.room.infra.LikeRepository;
import com.koliving.api.room.infra.RoomRepository;
import com.koliving.api.user.User;
import com.koliving.api.user.domain.Notification;
import com.koliving.api.user.domain.User;
import com.koliving.api.user.infra.UserRepository;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
Expand All @@ -45,6 +52,8 @@ public class RoomService {
private final RoomRepository roomRepository;
private final ImageFileRepository imageFileRepository;
private final LikeRepository likeRepository;
private final IEmailService emailService;
private final FrontProperties frontProperties;

public List<RoomResponse> list() {
return roomRepository.findAllWithUser()
Expand Down Expand Up @@ -144,4 +153,17 @@ private Room getRoom(Long id) {
return roomRepository.findByIdWithUser(id)
.orElseThrow(() -> new KolivingServiceException(RECORD_NOT_EXIST));
}

@Transactional
public void contact(RoomContactRequest request, User user) {
final Room room = getRoom(request.roomId());
final Notification notification = Notification.of(user, room.getUser());
room.getUser().addReceivedNotification(notification);
emailService.sendRoomContact("[email protected]", request.contactInfo(), request.message(), user, getRoomDetailUrl(room.getId()));
}


public String getRoomDetailUrl(Long roomId) {
return String.format("%s/room/%d", frontProperties.getOrigin(), roomId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@


import com.koliving.api.room.domain.Furnishing;
import io.swagger.v3.oas.annotations.media.Schema;

public record FurnishingResponse(Long id, String desc) {
@Schema(description = "가구 정보 조회")
public record FurnishingResponse(
@Schema(description = "가구 고유 ID")
Long id,

@Schema(description = "가구 설명")
String desc
) {

public static FurnishingResponse valueOf(Furnishing entity) {
return new FurnishingResponse(
Expand Down
Loading

0 comments on commit bb9c64c

Please sign in to comment.