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

[FEAT] DeviceToken Entity + Save Logic #84

Merged
merged 7 commits into from
Dec 29, 2023
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
3 changes: 3 additions & 0 deletions backend/src/docs/auth.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ operation::post save member[snippets='http-request,http-response']

=== 저장된 멤버 로그인
operation::post login[snippets='http-request,http-response']

=== 디바이스 토큰 업데이트
operation::post device token[snippets='http-request,http-response']
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.twtw.backend.config.converter;

import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConverterConfig {

@Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.twtw.backend.config.rabbitmq;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.twtw.backend.global.constant.RabbitMQConstant;
import com.twtw.backend.global.properties.RabbitMQProperties;

import lombok.RequiredArgsConstructor;
Expand All @@ -17,31 +18,49 @@
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Profile("!test")
@EnableRabbit
@Configuration
@RequiredArgsConstructor
public class RabbitMQConfig {

private static final String QUEUE_NAME = "map.queue";
private static final String EXCHANGE_NAME = "map";
private static final String ROUTING_KEY = "plan.*";
private final RabbitMQProperties rabbitMQProperties;
private final ObjectMapper objectMapper;

@Bean
public Queue queue() {
return new Queue(QUEUE_NAME, true);
public Queue locationQueue() {
return new Queue(RabbitMQConstant.LOCATION_QUEUE.getName(), true);
}

@Bean
public TopicExchange topicExchange() {
return new TopicExchange(EXCHANGE_NAME);
public TopicExchange locationTopicExchange() {
return new TopicExchange(RabbitMQConstant.LOCATION_EXCHANGE.getName());
}

@Bean
public Binding binding(final Queue queue, final TopicExchange topicExchange) {
return BindingBuilder.bind(queue).to(topicExchange).with(ROUTING_KEY);
public Binding locationBinding() {
return BindingBuilder.bind(locationQueue())
.to(locationTopicExchange())
.with(RabbitMQConstant.LOCATION_ROUTING_KEY.getName());
}

@Bean
public Queue notificationQueue() {
return new Queue(RabbitMQConstant.NOTIFICATION_QUEUE.getName(), true);
}

@Bean
public TopicExchange notificationTopicExchange() {
return new TopicExchange(RabbitMQConstant.NOTIFICATION_EXCHANGE.getName());
}

@Bean
public Binding notificationBinding() {
return BindingBuilder.bind(notificationQueue())
.to(notificationTopicExchange())
.with(RabbitMQConstant.NOTIFICATION_ROUTING_KEY.getName());
}

@Bean
Expand All @@ -63,20 +82,20 @@ public Jackson2JsonMessageConverter jsonMessageConverter() {
public RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
rabbitTemplate.setMessageConverter(jsonMessageConverter());
rabbitTemplate.setRoutingKey(QUEUE_NAME);
return rabbitTemplate;
}

@Bean
public RabbitAdmin rabbitAdmin(
final ConnectionFactory connectionFactory,
final Queue queue,
final TopicExchange topicExchange,
final Binding binding) {
public RabbitAdmin rabbitAdmin(final ConnectionFactory connectionFactory) {
final RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.declareQueue(queue);
rabbitAdmin.declareExchange(topicExchange);
rabbitAdmin.declareBinding(binding);

rabbitAdmin.declareQueue(locationQueue());
rabbitAdmin.declareExchange(locationTopicExchange());
rabbitAdmin.declareBinding(locationBinding());

rabbitAdmin.declareQueue(notificationQueue());
rabbitAdmin.declareExchange(notificationTopicExchange());
rabbitAdmin.declareBinding(notificationBinding());
return rabbitAdmin;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
import com.twtw.backend.domain.member.entity.Member;
import com.twtw.backend.domain.member.service.AuthService;
import com.twtw.backend.domain.member.service.MemberService;
import com.twtw.backend.domain.notification.dto.NotificationRequest;
import com.twtw.backend.domain.notification.messagequeue.FcmProducer;
import com.twtw.backend.global.constant.NotificationBody;
import com.twtw.backend.global.constant.NotificationTitle;
import com.twtw.backend.global.exception.EntityNotFoundException;

import lombok.RequiredArgsConstructor;
Expand All @@ -27,11 +31,22 @@ public class FriendService {
private final FriendMapper friendMapper;
private final MemberService memberService;
private final AuthService authService;
private final FcmProducer fcmProducer;

public void addRequest(final FriendRequest friendRequest) {
final Member loginMember = authService.getMemberByJwt();
final Member member = memberService.getMemberById(friendRequest.getMemberId());
friendRepository.save(friendMapper.toEntity(loginMember, member));

sendNotification(member.getDeviceTokenValue(), loginMember.getNickname());
}

private void sendNotification(final String deviceToken, final String nickname) {
fcmProducer.sendNotification(
new NotificationRequest(
deviceToken,
NotificationTitle.FRIEND_REQUEST_TITLE.getName(),
NotificationBody.FRIEND_REQUEST_BODY.toNotificationBody(nickname)));
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
import com.twtw.backend.domain.member.entity.Member;
import com.twtw.backend.domain.member.service.AuthService;
import com.twtw.backend.domain.member.service.MemberService;
import com.twtw.backend.domain.notification.service.FcmService;
import com.twtw.backend.domain.notification.dto.NotificationRequest;
import com.twtw.backend.domain.notification.messagequeue.FcmProducer;
import com.twtw.backend.global.constant.NotificationBody;
import com.twtw.backend.global.constant.NotificationTitle;
import com.twtw.backend.global.exception.EntityNotFoundException;

import org.springframework.stereotype.Service;
Expand All @@ -27,25 +30,23 @@ public class GroupService {
private final GroupRepository groupRepository;
private final GroupMemberRepository groupMemberRepository;
private final AuthService authService;

private final MemberService memberService;
private final GroupMapper groupMapper;

private final FcmService fcmService;
private final FcmProducer fcmProducer;

public GroupService(
GroupRepository groupRepository,
GroupMemberRepository groupMemberRepository,
AuthService authService,
MemberService memberService,
GroupMapper groupMapper,
FcmService fcmService) {
FcmProducer fcmProducer) {
this.groupRepository = groupRepository;
this.groupMemberRepository = groupMemberRepository;
this.authService = authService;
this.memberService = memberService;
this.groupMapper = groupMapper;
this.fcmService = fcmService;
this.fcmProducer = fcmProducer;
}

public GroupInfoResponse getGroupById(UUID groupId) {
Expand Down Expand Up @@ -117,11 +118,20 @@ public GroupInfoResponse inviteGroup(InviteGroupRequest inviteGroupRequest) {
memberService.getMembersByIds(inviteGroupRequest.getFriendMemberIds());
group.inviteAll(friends);

// invite push Alert
String groupName = group.getName();
friends.forEach(friend -> sendNotification(friend.getDeviceTokenValue(), groupName));

return groupMapper.toGroupInfo(group);
}

private void sendNotification(final String deviceToken, final String groupName) {
fcmProducer.sendNotification(
new NotificationRequest(
deviceToken,
NotificationTitle.GROUP_REQUEST_TITLE.getName(),
NotificationBody.GROUP_REQUEST_BODY.toNotificationBody(groupName)));
}

public GroupInfoResponse getGroupInfoResponse(Group group) {
return groupMapper.toGroupInfo(group);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.twtw.backend.domain.location.dto.request.LocationRequest;
import com.twtw.backend.domain.location.service.LocationService;
import com.twtw.backend.global.constant.RabbitMQConstant;

import lombok.RequiredArgsConstructor;

Expand All @@ -17,8 +18,6 @@
@RequiredArgsConstructor
public class LocationController {

private static final String EXCHANGE_NAME = "map";
private static final String ROUTING_KEY = "plan.";
private final RabbitTemplate rabbitTemplate;
private final LocationService locationService;

Expand All @@ -27,8 +26,8 @@ public void share(
@DestinationVariable final UUID planId,
@Payload final LocationRequest locationRequest) {
rabbitTemplate.convertAndSend(
EXCHANGE_NAME,
ROUTING_KEY + planId,
RabbitMQConstant.LOCATION_EXCHANGE.getName(),
RabbitMQConstant.LOCATION_ROUTING_KEY_PREFIX.getName() + planId,
locationService.addInfo(planId, locationRequest));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.twtw.backend.domain.member.controller;

import com.twtw.backend.domain.member.dto.request.DeviceTokenRequest;
import com.twtw.backend.domain.member.dto.request.MemberSaveRequest;
import com.twtw.backend.domain.member.dto.request.OAuthRequest;
import com.twtw.backend.domain.member.dto.request.TokenRequest;
Expand Down Expand Up @@ -43,4 +44,10 @@ public ResponseEntity<AfterLoginResponse> afterSocialLogin(
@RequestBody @Valid OAuthRequest request) {
return ResponseEntity.status(HttpStatus.OK).body(authService.getTokenByOAuth(request));
}

@PostMapping("/device")
public ResponseEntity<Void> updateDeviceToken(@RequestBody DeviceTokenRequest request) {
authService.updateDeviceToken(request);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public ResponseEntity<DuplicateNicknameResponse> duplicateNickname(@PathVariable
return ResponseEntity.ok(memberService.duplicateNickname(name));
}

@GetMapping()
@GetMapping
public ResponseEntity<SearchMemberResponse> searchMemberByNickname(
@RequestParam(name = "nickname") String nickname) {
return ResponseEntity.ok(memberService.getMemberByNickname(nickname));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.twtw.backend.domain.member.dto.request;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class DeviceTokenRequest {
private String deviceToken;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.twtw.backend.domain.member.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -12,7 +11,7 @@
@AllArgsConstructor
public class MemberSaveRequest {
@NotBlank private String nickname;
@NotNull private String profileImage;

@NotBlank private String profileImage;
@NotBlank private String deviceToken;
private OAuthRequest oauthRequest;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.twtw.backend.domain.member.entity;

import com.twtw.backend.global.audit.AuditListener;
import com.twtw.backend.global.audit.Auditable;
import com.twtw.backend.global.audit.BaseTime;

import jakarta.persistence.*;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import org.hibernate.annotations.Where;

import java.util.UUID;

@Entity
@Getter
@Where(clause = "deleted_at is null")
@EntityListeners(AuditListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class DeviceToken implements Auditable {

@Id
@GeneratedValue(generator = "uuid2")
@Column(name = "id", columnDefinition = "BINARY(16)")
private UUID id;

private String deviceToken;

@OneToOne(mappedBy = "deviceToken")
private Member member;

@Setter
@Embedded
@Column(nullable = false)
private BaseTime baseTime;

public DeviceToken(String deviceToken) {
this.deviceToken = deviceToken;
}

public void organizeMember(Member member) {
this.member = member;
}
}
Loading