Skip to content

Commit

Permalink
[OING-300] feat: 콕 찌르기 API 구현 (#222)
Browse files Browse the repository at this point in the history
* feat: implementing pick..

* feat: implement pick feature

* feat: add picked members api

* feat: implement picked api

* feat: implement GET api

* feat: check member exists

* feat: update api specs

* feat: concat sql queries

* feat: seperate notification
  • Loading branch information
CChuYong authored Apr 8, 2024
1 parent 3a286c6 commit b45164b
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 4 deletions.
10 changes: 9 additions & 1 deletion common/src/main/java/com/oing/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,15 @@ public enum ErrorCode {
/**
* Deep Link Related Errors
*/
LINK_NOT_VALID("DL0001", "Link is not valid");
LINK_NOT_VALID("DL0001", "Link is not valid"),

/**
* Member Pick Related Errors
*/
ALREADY_PICKED_TODAY("MP0001", "Already picked today"),
MEMBER_ALREADY_UPLOADED_POST("MP0002", "Member already uploaded post"),

;


private final String code;
Expand Down
11 changes: 11 additions & 0 deletions common/src/main/java/com/oing/service/PostBridge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.oing.service;

/**
* no5ing-server
* User: CChuYong
* Date: 3/31/24
* Time: 5:47 PM
*/
public interface PostBridge {
boolean isUploadedToday(String familyId, String memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `member_pick` (
`pick_id` CHAR(26) NOT NULL COMMENT 'ULID',
`family_id` CHAR(26) NOT NULL COMMENT 'ULID',
`from_member_id` CHAR(26) NOT NULL COMMENT 'ULID',
`date` DATE NOT NULL,
`to_member_id` CHAR(26) NOT NULL COMMENT 'ULID',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX `member_pick_idx1` (`family_id`, `from_member_id`, `date`, `to_member_id`),
INDEX `member_pick_idx2` (`family_id`, `date`, `to_member_id`),
PRIMARY KEY (`pick_id`)
) DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci comment '사용자콕찌르기';
68 changes: 65 additions & 3 deletions member/src/main/java/com/oing/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
package com.oing.controller;

import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.MulticastMessage;
import com.google.firebase.messaging.Notification;
import com.oing.domain.Member;
import com.oing.domain.MemberPick;
import com.oing.domain.PaginationDTO;
import com.oing.dto.request.PreSignedUrlRequest;
import com.oing.dto.request.QuitMemberRequest;
import com.oing.dto.request.UpdateMemberNameRequest;
import com.oing.dto.request.UpdateMemberProfileImageUrlRequest;
import com.oing.dto.response.*;
import com.oing.exception.AuthorizationFailedException;
import com.oing.exception.PickFailedAlreadyUploadedException;
import com.oing.restapi.MemberApi;
import com.oing.service.MemberDeviceService;
import com.oing.service.MemberQuitReasonService;
import com.oing.service.MemberService;
import com.oing.service.*;
import com.oing.util.FCMNotificationUtil;
import com.oing.util.PreSignedUrlGenerator;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;

import java.security.InvalidParameterException;
import java.util.List;

@Controller
@RequiredArgsConstructor
public class MemberController implements MemberApi {

private final PreSignedUrlGenerator preSignedUrlGenerator;
private final PostBridge postBridge;
private final MemberPickService memberPickService;
private final MemberService memberService;
private final MemberDeviceService memberDeviceService;
private final MemberQuitReasonService memberQuitReasonService;
private final FCMNotificationService fcmNotificationService;

@Override
public PaginationResponse<FamilyMemberProfileResponse> getFamilyMembersProfiles(
Expand Down Expand Up @@ -119,6 +127,60 @@ public DefaultResponse deleteMember(String memberId, String loginMemberId, QuitM
return DefaultResponse.ok();
}

@Transactional
@Override
public DefaultResponse pickMember(String memberId, String loginMemberId, String loginFamilyId) {
if (postBridge.isUploadedToday(loginFamilyId, memberId)) {
throw new PickFailedAlreadyUploadedException();
}
Member toMember = memberService.findMemberById(memberId);
MemberPick memberPick = memberPickService.pickMember(loginFamilyId, loginMemberId, memberId);

Member fromMember = memberService.findMemberById(memberPick.getFromMemberId());

List<String> tokens = memberDeviceService.getFcmTokensByMemberId(toMember.getId());
if (!tokens.isEmpty()) {
Notification notification = FCMNotificationUtil
.buildNotification(String.format("%s님, 살아있나요?", toMember.getName()),
String.format("%s님이 당신의 생존을 궁금해해요.", fromMember.getName()));
MulticastMessage message = MulticastMessage.builder()
.setNotification(notification)
.addAllTokens(tokens)
.setApnsConfig(FCMNotificationUtil.buildApnsConfig())
.setAndroidConfig(FCMNotificationUtil.buildAndroidConfig())
.build();
fcmNotificationService.sendMulticastMessage(message);
}

return DefaultResponse.ok();
}

@Override
public ArrayResponse<MemberResponse> getPickMembers(String memberId, String loginFamilyId) {
//나를 찌른 사람들
List<MemberPick> pickedMembers = memberPickService.getPickMembers(loginFamilyId, memberId);
return new ArrayResponse<>(
pickedMembers
.stream()
.map(memberPick -> memberService.findMemberById(memberPick.getFromMemberId()))
.map(MemberResponse::of)
.toList()
);
}

@Override
public ArrayResponse<MemberResponse> getPickedMembers(String memberId, String loginFamilyId) {
//내가 찌른 사람들
List<MemberPick> pickedMembers = memberPickService.getPickedMembers(loginFamilyId, memberId);
return new ArrayResponse<>(
pickedMembers
.stream()
.map(memberPick -> memberService.findMemberById(memberPick.getFromMemberId()))
.map(MemberResponse::of)
.toList()
);
}

private void validateMemberId(String memberId, String loginMemberId) {
if (!loginMemberId.equals(memberId)) {
throw new AuthorizationFailedException();
Expand Down
37 changes: 37 additions & 0 deletions member/src/main/java/com/oing/domain/MemberPick.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.oing.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.*;

import java.time.LocalDate;

/**
* no5ing-server
* User: CChuYong
* Date: 2024/04/02
* Time: 11:31 AM
*/
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@Getter
@Entity(name = "member_pick")
public class MemberPick extends BaseEntity {
@Id
@Column(name = "pick_id", length = 26, columnDefinition = "CHAR(26)")
private String pickId;

@Column(name = "family_id", length = 26, columnDefinition = "CHAR(26)")
private String familyId;

@Column(name = "from_member_id", length = 26, columnDefinition = "CHAR(26)")
private String fromMemberId;

@Column(name = "date", columnDefinition = "DATE")
private LocalDate date;

@Column(name = "to_member_id", columnDefinition = "CHAR(26)")
private String toMemberId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.oing.exception;

/**
* no5ing-server
* User: CChuYong
* Date: 4/2/24
* Time: 12:03 PM
*/
public class AlreadyPickedMemberException extends DomainException{
public AlreadyPickedMemberException() {
super(ErrorCode.ALREADY_PICKED_TODAY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.oing.exception;

/**
* no5ing-server
* User: CChuYong
* Date: 4/2/24
* Time: 12:04 PM
*/
public class PickFailedAlreadyUploadedException extends DomainException {
public PickFailedAlreadyUploadedException() {
super(ErrorCode.MEMBER_ALREADY_UPLOADED_POST);
}
}
19 changes: 19 additions & 0 deletions member/src/main/java/com/oing/repository/MemberPickRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.oing.repository;

import com.oing.domain.MemberPick;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDate;
import java.util.List;

/**
* no5ing-server
* User: CChuYong
* Date: 4/2/24
* Time: 11:54 AM
*/
public interface MemberPickRepository extends JpaRepository<MemberPick, String> {
MemberPick findByFamilyIdAndFromMemberIdAndDateAndToMemberId(String familyId, String fromMemberId, LocalDate date, String toMemberId);
List<MemberPick> findAllByFamilyIdAndDateAndToMemberId(String familyId, LocalDate date, String toMemberId);
List<MemberPick> findAllByFamilyIdAndDateAndFromMemberId(String familyId, LocalDate date, String fromMemberId);
}
40 changes: 40 additions & 0 deletions member/src/main/java/com/oing/restapi/MemberApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,44 @@ DefaultResponse deleteMember(
@RequestBody(required = false) //for api version compatibility
QuitMemberRequest request
);

@Operation(summary = "콕 찌르기", description = "사용자를 콕 찌릅니다.")
@PostMapping("/{memberId}/pick")
DefaultResponse pickMember(
@Parameter(description = "콕 찌를 회원 ID", example = "01HGW2N7EHJVJ4CJ999RRS2E97")
@PathVariable
String memberId,

@Parameter(hidden = true)
@LoginMemberId
String loginMemberId,

@Parameter(hidden = true)
@LoginFamilyId
String loginFamilyId
);

@Operation(summary = "사용자를 콕 찌른 사람들", description = "오늘 사용자를 콕 찌른 사람 목록을 반환합니다.")
@GetMapping("/{memberId}/pick")
ArrayResponse<MemberResponse> getPickMembers(
@Parameter(description = "콕 찌를 회원 ID", example = "01HGW2N7EHJVJ4CJ999RRS2E97")
@PathVariable
String memberId,

@Parameter(hidden = true)
@LoginFamilyId
String loginFamilyId
);

@Operation(summary = "사용자가 콕 찌른 사람들", description = "오늘 사용자가 콕 찌른 사람 목록을 반환합니다.")
@GetMapping("/{memberId}/picked")
ArrayResponse<MemberResponse> getPickedMembers(
@Parameter(description = "조회할 회원 ID", example = "01HGW2N7EHJVJ4CJ999RRS2E97")
@PathVariable
String memberId,

@Parameter(hidden = true)
@LoginFamilyId
String loginFamilyId
);
}
54 changes: 54 additions & 0 deletions member/src/main/java/com/oing/service/MemberPickService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.oing.service;

import com.oing.domain.MemberPick;
import com.oing.exception.AlreadyPickedMemberException;
import com.oing.repository.MemberPickRepository;
import com.oing.util.IdentityGenerator;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

/**
* no5ing-server
* User: CChuYong
* Date: 4/2/24
* Time: 11:58 AM
*/
@RequiredArgsConstructor
@Service
public class MemberPickService {
private final MemberPickRepository memberPickRepository;
private final IdentityGenerator identityGenerator;

@Transactional
public MemberPick pickMember(String familyId, String fromMemberId, String toMemberId) {
LocalDate today = LocalDate.now();
MemberPick priorMemberPick = memberPickRepository
.findByFamilyIdAndFromMemberIdAndDateAndToMemberId(familyId, fromMemberId, today, toMemberId);
if(priorMemberPick != null) {
throw new AlreadyPickedMemberException();
}

MemberPick newMemberPick = new MemberPick(
identityGenerator.generateIdentity(),
familyId,
fromMemberId,
today,
toMemberId
);
return memberPickRepository.save(newMemberPick);
}

public List<MemberPick> getPickMembers(String familyId, String pickedMemberId) {
LocalDate today = LocalDate.now();
return memberPickRepository.findAllByFamilyIdAndDateAndToMemberId(familyId, today, pickedMemberId);
}

public List<MemberPick> getPickedMembers(String familyId, String pickerMemberId) {
LocalDate today = LocalDate.now();
return memberPickRepository.findAllByFamilyIdAndDateAndFromMemberId(familyId, today, pickerMemberId);
}
}
25 changes: 25 additions & 0 deletions post/src/main/java/com/oing/service/PostBridgeImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.oing.service;

import com.oing.repository.PostRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;

/**
* no5ing-server
* User: CChuYong
* Date: 3/31/24
* Time: 5:48 PM
*/
@RequiredArgsConstructor
@Service
public class PostBridgeImpl implements PostBridge {
private final PostRepository postRepository;

@Override
public boolean isUploadedToday(String familyId, String memberId) {
LocalDate today = LocalDate.now();
return postRepository.existsByMemberIdAndFamilyIdAndCreatedAt(memberId, familyId, today);
}
}

0 comments on commit b45164b

Please sign in to comment.