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

[RELEASE/3.0.0] release: v3.0.0 #254

Merged
merged 35 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
25aa14b
chore: update ECS Service name
CChuYong Apr 3, 2024
530c929
chore: update production ecs
CChuYong Apr 3, 2024
8433e61
refactor: Translate english error message to Korean (#224)
Kwon770 Apr 8, 2024
e96aff1
[OING-300] feat: 콕 찌르기 API 구현 (#222)
CChuYong Apr 8, 2024
6187447
[OING-289] refactor: Member 모듈의 안티패턴과 짜잘한 부분 리팩토링 (#223)
Kwon770 Apr 8, 2024
d32295f
[OING-305] feature: Mission 도입에 맞춰 Post 테이블 변경 (#225)
Ji-soo708 Apr 10, 2024
92647ac
hotfix: add Enumerated annotation (#226)
Ji-soo708 Apr 10, 2024
5c60020
hotfix: Type 필드 추가하면서 발생하는 에러 해결 (#227)
Ji-soo708 Apr 10, 2024
f986a1b
[OING-302] feature: 미션 포스트 작성/조회 모킹 API 추가 (#228)
Ji-soo708 Apr 15, 2024
de7c9bb
feature: change validateUploadTime's bound (#229)
Ji-soo708 Apr 15, 2024
053fb49
[OING-307] feat: Mission 모듈 추가 (#230)
Kwon770 Apr 16, 2024
f740c24
fix: Fix broken flyway db.migration V202404152221__create_Mission_mod…
Kwon770 Apr 16, 2024
6447000
[OING-309] feat: Mission, DailyMissionHistory의 API Mocking과 Service f…
Kwon770 Apr 16, 2024
8b0d6b7
[OING-308] feat: 가족구성원 랭킹 API 모킹 (#231)
Kwon770 Apr 17, 2024
dd7cb38
feature: add set type column value sql (#234)
Ji-soo708 Apr 17, 2024
b7a2598
[OING-314] feature: 회원 생존신고 게시글 업로드 여부 / 회원 미션 참여 가능 여부 확인 mock API 추…
Ji-soo708 Apr 17, 2024
285a391
[OING-311] feat: 뷰 기반 API 중 메인 페이지 API 구현 (#237)
CChuYong Apr 20, 2024
fe0e185
[OING-315] feature: 미션 게시글 업로드/조회 API 구현 (검증 로직 제외) (#236)
Ji-soo708 Apr 21, 2024
901ad99
[OING-317] refactor: 뷰 기반 API를 둘러싼 교통정리 1 (#238)
Kwon770 Apr 21, 2024
6bf5328
[OING-316] feature: 가족 중 몇명이 더 올리면 미션 참여 가능한지 반환하는 API 구현 (#239)
Ji-soo708 Apr 24, 2024
15d30fe
[OING-304] feature: 회원 생존신고 게시글 업로드 여부 / 가족 미션 참여 가능 여부 확인 API 구현 (#240)
Ji-soo708 Apr 24, 2024
0d714b0
[OING-320] refactor: 가족 중 몇명이 더 올리면 미션 참여 가능한지 반환하는 API 로직 리팩터링 (#242)
Ji-soo708 Apr 24, 2024
e0a8a1d
[OING-319] feat: 메인 페이지에서 필요한 추가 요청받은 API 구현/모킹 (#241)
Kwon770 Apr 24, 2024
458d0e9
refactor: add today date log to validateUserHasNotCreatedPost logic (…
Ji-soo708 Apr 25, 2024
3ad76b1
[OING-324] refactor: NighttimeMainPage의 displayRank 필드는 늘 NULL이 되도록 수…
Kwon770 Apr 27, 2024
f1e6a5a
[OING-323] feat: 일간 캘린더 API 추가 (#244)
Kwon770 Apr 28, 2024
f123f4a
refactor: Refactoring getXXXCalendar mapping stragey from params to u…
Kwon770 Apr 28, 2024
d55c0a8
hotfix: Restore getMonthlyCalendar API as deprecated lagacy for compa…
Kwon770 Apr 28, 2024
e4d1c0e
[OING-328] feat: DailyCalendarResponse에 PostResponse 필드 추가 (프론트 요청) (…
Kwon770 May 2, 2024
321ab3e
[OING-325] feat: 가족구성원 랭킹 API 기능 구현 (#246)
Kwon770 May 6, 2024
67a0834
[OING-329] refactor: 미션 검증 로직 중에서 가족 구성원 수 계산할 때, 어제 날짜 기준으로 세도록 수정 (…
Ji-soo708 May 6, 2024
70987cb
[OING-326] feat: 일일 미션 배치 구현 및 테스트코드 추가 (#249)
Kwon770 May 7, 2024
3dbae8b
fix: Add missing PathVariable at input paramter of getMissionByMissio…
Kwon770 May 9, 2024
d2ff054
[OING-330] feat: 가족 구성원 랭킹에서 '지난 날 생존신고 보기'에 대한 보충 정책 반영 (#250)
Kwon770 May 9, 2024
a9e3637
[OING-318] feature: 미션 게시글 등록 기능에 미션 등록 권한 검증 로직 추가 (#251)
Ji-soo708 May 9, 2024
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: 2 additions & 1 deletion .github/workflows/dev-cicd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ on:
- 'common/**' # Common 모듈 변경
- 'family/**' # Family 모듈 변경
- 'post/**' # Post 모듈 변경
- 'mission/**' # Mission 모듈 변경
- '.github/workflows/**' # 워크플로우와 관련된 파일이 변경된 경우
- 'build.gradle' # Parent Gradle 모듈 설정이 변경된 경우
- 'settings.gradle' # Parent Gradle 설정이 변경된 경우

env:
ECR_REPOSITORY_NAME: bibbi-backend-dev
SPRING_PROFILE: dev
ECS_SERVICE: bibbi-backend-dev
ECS_SERVICE: bibbi-backend-development
ECS_CLUSTER: bibbi-cluster-prod
ECR_TASK_DEFINITION: bibbi-backend-dev

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/prod-cicd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ on:
- 'common/**' # Common 모듈 변경
- 'family/**' # Family 모듈 변경
- 'post/**' # Post 모듈 변경
- 'mission/**' # Mission 모듈 변경
- '.github/workflows/**' # 워크플로우와 관련된 파일이 변경된 경우
- 'build.gradle' # Parent Gradle 모듈 설정이 변경된 경우
- 'settings.gradle' # Parent Gradle 설정이 변경된 경우

env:
ECR_REPOSITORY_NAME: bibbi-backend-prod
SPRING_PROFILE: prod
ECS_SERVICE: bibbi-backend
ECS_SERVICE: bibbi-backend-prod
ECS_CLUSTER: bibbi-cluster-prod
ECR_TASK_DEFINITION: bibbi-backend-prod

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
.DS_Store

### STS ###
.apt_generated
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.oing.exception;

public class AuthorizationFailedException extends DomainException {
public AuthorizationFailedException(ErrorCode errorCode) {
super(errorCode);
}

public AuthorizationFailedException() {
super(ErrorCode.AUTHORIZATION_FAILED);
}
Expand Down
68 changes: 45 additions & 23 deletions common/src/main/java/com/oing/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,77 @@ public enum ErrorCode {
*/
APP_UPDATE_REQUIRED("AV0001", "Update Required"),
APP_KEY_NOT_FOUND("AV0002", "App Key Not Found"),

/**
* Auth Related Errors
*/
AUTHENTICATION_FAILED("AU0001", "Authentication failed"),
TOKEN_AUTHENTICATION_FAILED("AU0002", "Token Authentication failed"),
AUTHORIZATION_FAILED("AU0003", "No Permission"),
REFRESH_TOKEN_INVALID("AU0004", "Refresh Token is invalid"),
TOKEN_EXPIRED("AU0005", "Token is expired"),
AUTHENTICATION_FAILED("AU0001", "인증에 실패했습니다."),
TOKEN_AUTHENTICATION_FAILED("AU0002", "토큰을 통한 인증에 실패했습니다."),
AUTHORIZATION_FAILED("AU0003", "권한이 없습니다."),
REFRESH_TOKEN_INVALID("AU0004", "유효하지 않은 리프레쉬 토큰입니다."),
TOKEN_EXPIRED("AU0005", "토큰이 만료됐습니다."),
UNAUTHORIZED_MEMBER_USED("AU0006", "인증되지 않은/권한이 없는 회원에 접근하였습니다."),

/**
* Member Related Errors
*/
MEMBER_NOT_FOUND("MB0001", "Member not found"),
MEMBER_ALREADY_EXISTS("MB0002", "Member With That Credentials Already Exists"),
MEMBER_NOT_FOUND("MB0001", "찾을 수 없는 회원입니다."),
MEMBER_ALREADY_EXISTS("MB0002", "이미 존재하는 회원입니다."),
INVALID_MEMBER_NAME_LENGTH("MB0003", "회원 이름의 길이가 유효하지 않습니다."),
/**
* Post Related Errors
*/
POST_NOT_FOUND("PO0001", "Post not found"),
POST_NOT_FOUND("PO0001", "존재하지 않는 게시글입니다."),
INVALID_UPLOAD_TIME("PO0002", "게시글 업로드가 허용되지 않은 시간대입니다."),
DUPLICATE_SURVIVAL_POST_UPLOAD("PO0003", "이미 생존신고 게시글을 업로드했습니다."),
DUPLICATE_MISSION_POST_UPLOAD("PO0004", "이미 미션 게시글을 업로드했습니다."),
MISSION_POST_ACCESS_DENIED_FAMILY("PO0005", "미션 게시글 기능에 접근할 수 없는 가족입니다."),
MISSION_POST_CREATE_ACCESS_DENIED_MEMBER("PO0006", "미션 게시글 업로드 권한이 없는 회원입니다."),

/**
* Emoji Related Errors
*/
EMOJI_ALREADY_EXISTS("EM0001", "Emoji already exists"),
EMOJI_NOT_FOUND("EM0002", "Emoji not found"),
EMOJI_ALREADY_EXISTS("EM0001", "이미 존재하는 이모지입니다."),
EMOJI_NOT_FOUND("EM0002", "존재하지 않는 이모지입니다."),

/**
* Comment Related Errors
*/
POST_COMMENT_NOT_FOUND("CM0001", "Comment not found"),
POST_COMMENT_NOT_FOUND("CM0001", "존재하지 않는 댓글입니다."),

/**
* Family Related Errors
*/
FAMILY_NOT_FOUND("FM0001", "Family not found"),
ALREADY_IN_FAMILY("FM0002", "Already in family"),
FAMILY_NOT_FOUND("FM0001", "존재하지 않는 가족입니다."),
ALREADY_IN_FAMILY("FM0002", "이미 가족에 가입되어 있습니다."),

/**
* Post Related Errors
* Real-Emoji Related Errors
*/
INVALID_UPLOAD_TIME("PO0001", "Invalid Upload Time. The request is outside the valid time range" +
"(from 12:00 AM yesterday to 12:00 AM today)."),
DUPLICATE_POST_UPLOAD("PO0002", "Duplicate Post Upload"),
REAL_EMOJI_NOT_FOUND("RE0001", "존재하지 않는 리얼이모지입니다."),
REAL_EMOJI_ALREADY_EXISTS("RE0002", "이미 존재하는 리얼이모지입니다."),
REGISTERED_REAL_EMOJI_NOT_FOUND("RE0003", "등록된 리얼이모지가 아닙니다."),
DUPLICATE_REAL_EMOJI("RE0004", "이미 등록된 리얼이모지입니다."),

/**
* Real-Emoji Related Errors
* Mission Related Errors
*/
REAL_EMOJI_NOT_FOUND("RE0001", "Real-Emoji not found"),
REAL_EMOJI_ALREADY_EXISTS("RE0002", "Real-Emoji already exists"),
REGISTERED_REAL_EMOJI_NOT_FOUND("RE0003", "Registered Real-Emoji not found"),
DUPLICATE_REAL_EMOJI("RE0004", "Duplicate Real Emoji"),
MISSION_NOT_FOUND("MI0001", "존재하지 않는 미션입니다."),
DAILY_MISSION_HISTORY_NOT_FOUND("MI0002", "존재하지 않는 미션 히스토리입니다."),
DUPLICATED_DAILY_MISSION_HISTORY("MI0003", "이미 존재하는 미션 히스토리입니다."),

/**
* 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
4 changes: 4 additions & 0 deletions common/src/main/java/com/oing/service/MemberBridge.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.oing.service;

import java.util.List;

/**
* no5ing-server
* User: CChuYong
Expand Down Expand Up @@ -31,4 +33,6 @@ public interface MemberBridge {
* @return 삭제된 사용자인지 여부
*/
boolean isDeletedMember(String memberId);

List<String> getFamilyMembersIdsByFamilyId(String familyId);
}
12 changes: 12 additions & 0 deletions common/src/main/java/com/oing/service/MissionBridge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.oing.service;

import java.time.LocalDate;

public interface MissionBridge {

String getContentByMissionId(String missionId);

String getContentByDate(LocalDate date);

String getTodayMissionId();
}
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);
}
2 changes: 2 additions & 0 deletions gateway/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ dependencies {
implementation project(':member')
implementation project(':family')
implementation project(':post')
implementation project(':mission')

jacocoAggregation project(':common')
jacocoAggregation project(':member')
jacocoAggregation project(':family')
jacocoAggregation project(':post')
jacocoAggregation project(':mission')

implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import com.oing.component.SentryGateway;
import com.oing.domain.ErrorReportDTO;
import com.oing.dto.response.ErrorResponse;
import com.oing.exception.TokenNotValidException;
import com.oing.exception.DomainException;
import com.oing.exception.ErrorCode;
import com.oing.exception.*;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
Expand Down
81 changes: 62 additions & 19 deletions gateway/src/main/java/com/oing/controller/CalendarController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,33 @@

import com.oing.domain.BannerImageType;
import com.oing.domain.Post;
import com.oing.dto.response.ArrayResponse;
import com.oing.dto.response.BannerResponse;
import com.oing.dto.response.CalendarResponse;
import com.oing.dto.response.FamilyMonthlyStatisticsResponse;
import com.oing.domain.PostType;
import com.oing.dto.response.*;
import com.oing.restapi.CalendarApi;
import com.oing.service.FamilyService;
import com.oing.service.MemberService;
import com.oing.service.PostService;
import com.oing.service.*;
import com.oing.util.OptimizedImageUrlGenerator;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

import static com.oing.domain.PostType.MISSION;
import static com.oing.domain.PostType.SURVIVAL;

@Controller
@RequiredArgsConstructor
public class CalendarController implements CalendarApi {

private final PostController postController;
private final MissionBridge missionBridge;
private final MemberBridge memberBridge;

private final MemberService memberService;
private final PostService postService;
private final FamilyService familyService;
Expand All @@ -31,42 +37,78 @@ public class CalendarController implements CalendarApi {


@Override
public ArrayResponse<CalendarResponse> getMonthlyCalendar(String yearMonth, String familyId) {
public ArrayResponse<DailyCalendarResponse> getDailyCalendar(String yearMonthDay, String loginMemberId, String loginFamilyId) {
List<DailyCalendarResponse> dailyCalendarResponses = new ArrayList<>();
LocalDate date = LocalDate.parse(yearMonthDay, DateTimeFormatter.ISO_DATE);

Collection<PostResponse> survivalPosts = postController.fetchDailyFeeds(1, 10, date, null, "ASC", SURVIVAL, loginMemberId).results();
Collection<PostResponse> missionPosts = postController.fetchDailyFeeds(1, 10, date, null, "ASC", MISSION, loginMemberId).results();
String missionContent = missionBridge.getContentByDate(date);
boolean allFamilyMembersUploaded = getAllFamilyMembersUploaded(survivalPosts, loginFamilyId);

dailyCalendarResponses.addAll(convertToDailyCalendarResponse(survivalPosts, missionContent, allFamilyMembersUploaded));
dailyCalendarResponses.addAll(convertToDailyCalendarResponse(missionPosts, missionContent, allFamilyMembersUploaded));
return ArrayResponse.of(dailyCalendarResponses);
}

private boolean getAllFamilyMembersUploaded(Collection<PostResponse> survivalPosts, String familyId) {
HashSet<String> uploadedFamilyMembers = survivalPosts.stream().map(PostResponse::authorId).collect(Collectors.toCollection(HashSet::new));
List<String> familyMembersIds = memberService.getFamilyMembersIdsByFamilyIdAndJoinAtBefore(familyId, LocalDate.now());

return uploadedFamilyMembers.containsAll(familyMembersIds);
}

private List<DailyCalendarResponse> convertToDailyCalendarResponse(Collection<PostResponse> posts, String missionContent, boolean allFamilyMembersUploaded) {
return posts.stream().map(post -> switch (PostType.fromString(post.type())) {
case MISSION -> DailyCalendarResponse.of(post, missionContent, allFamilyMembersUploaded);
case SURVIVAL -> DailyCalendarResponse.of(post, null, allFamilyMembersUploaded);
}).toList();
}


@Override
public ArrayResponse<MonthlyCalendarResponse> getMonthlyCalendar(String yearMonth, String familyId) {
if (yearMonth == null) yearMonth = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));

LocalDate startDate = LocalDate.parse(yearMonth + "-01"); // yyyy-MM-dd 패턴으로 파싱
LocalDate endDate = startDate.plusMonths(1);

List<Post> daysLatestPosts = postService.findLatestPostOfEveryday(startDate, endDate, familyId);
List<CalendarResponse> calendarResponses = convertToCalendarResponse(daysLatestPosts, familyId);
return new ArrayResponse<>(calendarResponses);
List<MonthlyCalendarResponse> monthlyCalendarResponses = convertToMonthlyCalendarResponse(daysLatestPosts, familyId);
return new ArrayResponse<>(monthlyCalendarResponses);
}

private List<CalendarResponse> convertToCalendarResponse(List<Post> daysLatestPosts, String familyId) {
List<CalendarResponse> calendarResponses = new ArrayList<>();
private List<MonthlyCalendarResponse> convertToMonthlyCalendarResponse(List<Post> daysLatestPosts, String familyId) {
List<MonthlyCalendarResponse> monthlyCalendarResponses = new ArrayList<>();

for (Post dayLatestPost : daysLatestPosts) {
LocalDate postDate = dayLatestPost.getCreatedAt().toLocalDate();

// 탈퇴한 회원을 제외하고 allFamilyMembersUploaded 기본값이 true이므로, 탈퇴한 회원이 allFamilyMembersUploaded 계산에 영향을 미치지 않음
// edge case: 글을 업로드하지 않은 회원이 탈퇴하면, 과거 날짜들의 allFamilyMembersUploaded이 true로 변함 -> 핸들링할 수 없는 케이스
List<String> familyMembersIds = memberService.findFamilyMembersIdsByFamilyJoinAtBefore(familyId, postDate.plusDays(1));
List<String> familyMembersIds = memberService.getFamilyMembersIdsByFamilyIdAndJoinAtBefore(familyId, postDate.plusDays(1));
boolean allFamilyMembersUploaded = true;
for (String memberId : familyMembersIds) {
if (!postService.existsByMemberIdAndFamilyIdAndCreatedAt(memberId, familyId, postDate)) {
if (!postService.existsByMemberIdAndFamilyIdAndTypeAndCreatedAt(memberId, familyId, SURVIVAL, postDate)) {
allFamilyMembersUploaded = false;
break;
}
}

calendarResponses.add(new CalendarResponse(
monthlyCalendarResponses.add(new MonthlyCalendarResponse(
dayLatestPost.getCreatedAt().toLocalDate(),
dayLatestPost.getId(),
optimizedImageUrlGenerator.getThumbnailUrlGenerator(dayLatestPost.getPostImgUrl()),
allFamilyMembersUploaded
));
}
return calendarResponses;
return monthlyCalendarResponses;
}


@Override
public ArrayResponse<MonthlyCalendarResponse> getMonthlyCalendarLegacy(String yearMonth, String familyId) {
return getMonthlyCalendar(yearMonth, familyId);
}


Expand Down Expand Up @@ -95,9 +137,9 @@ public BannerResponse getBanner(String yearMonth, String familyId) {
boolean allFamilyMembersUploaded = true;

if (postService.existsByFamilyIdAndCreatedAt(familyId, startDate)) {
List<String> familyMembersIds = memberService.findFamilyMembersIdsByFamilyJoinAtBefore(familyId, startDate.plusDays(1));
List<String> familyMembersIds = memberService.getFamilyMembersIdsByFamilyIdAndJoinAtBefore(familyId, startDate.plusDays(1));
for (String memberId : familyMembersIds) {
if (!postService.existsByMemberIdAndFamilyIdAndCreatedAt(memberId, familyId, startDate)) {
if (!postService.existsByMemberIdAndFamilyIdAndTypeAndCreatedAt(memberId, familyId, SURVIVAL, startDate)) {
allFamilyMembersUploaded = false;
break;
}
Expand Down Expand Up @@ -161,13 +203,14 @@ private BannerImageType getBannerImageType(int familyLevel) {
return bannerImageType;
}


@Override
public FamilyMonthlyStatisticsResponse getSummary(String yearMonth, String loginMemberId) {
String[] yearMonthArray = yearMonth.split("-");
int year = Integer.parseInt(yearMonthArray[0]);
int month = Integer.parseInt(yearMonthArray[1]);

String familyId = memberService.findFamilyIdByMemberId(loginMemberId);
String familyId = memberService.getFamilyIdByMemberId(loginMemberId);
long monthlyPostCount = postService.countMonthlyPostByFamilyId(year, month, familyId);
return new FamilyMonthlyStatisticsResponse((int) monthlyPostCount);
}
Expand Down
Loading
Loading