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(#93) housework todo #98

Merged
merged 10 commits into from
Nov 5, 2023
1 change: 1 addition & 0 deletions heachi-core/housework-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies {
implementation project(':heachi-support:common')
implementation project(':heachi-support:logging')
implementation project(':heachi-support:external-clients')
implementation project(':heachi-support:aws-s3') // aws s3 모듈
implementation project(':heachi-domain-mysql')
implementation project(':heachi-domain-redis')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import com.heachi.admin.common.response.JsonResult;
import com.heachi.external.clients.auth.response.UserInfoResponse;
import com.heachi.housework.api.controller.housework.todo.request.VerifyTodoRequest;
import com.heachi.housework.api.service.auth.AuthExternalService;
import com.heachi.housework.api.service.housework.todo.TodoService;
import com.heachi.housework.api.service.housework.todo.request.TodoSelectRequest;
import com.heachi.housework.api.service.housework.todo.request.VerifyTodoServiceRequest;
import com.heachi.s3.api.service.AwsS3Service;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
Expand All @@ -19,6 +23,7 @@ public class TodoController {

private final AuthExternalService authExternalService;
private final TodoService todoService;
private final AwsS3Service awsS3Service;

// Todo List 가져오기
@GetMapping("/{groupId}")
Expand All @@ -30,4 +35,19 @@ public JsonResult<?> selectTodo(@RequestHeader(name = "Authorization") String au
return JsonResult.successOf(todoService.cachedSelectTodo(
TodoSelectRequest.builder().groupId(groupId).date(date).build()));
}

// 집안일 인증하기
@PostMapping("/verify")
public JsonResult<?> verifyTodo(@RequestHeader(name = "Authorization") String authorization,
@Valid @ModelAttribute(name = "verifyTodoRequest") VerifyTodoRequest verifyTodoRequest) {
jusung-c marked this conversation as resolved.
Show resolved Hide resolved
UserInfoResponse userInfo = authExternalService.userAuthenticateAndGroupMatch(authorization, verifyTodoRequest.getGroupId());
String uploadedImageURL = awsS3Service.uploadImage(verifyTodoRequest.getFile());
todoService.verifyTodo(VerifyTodoServiceRequest.builder()
.verifyImageURL(uploadedImageURL)
.todoId(verifyTodoRequest.getTodoId())
.email(userInfo.getEmail())
.build());

return JsonResult.successOf("사진이 정상적으로 저장되었습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.heachi.housework.api.controller.housework.todo.request;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.web.multipart.MultipartFile;

@Getter
@ToString
@AllArgsConstructor
public class VerifyTodoRequest {
@NotNull
private MultipartFile file; // (필수) 사진
@NotNull
private Long todoId; // (필수) 집안일 아이디
@NotNull
private Long groupId; // (필수) 그룹 아이디
private String comment; // (선택) 사진에 대한 설명
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.heachi.housework.api.service.housework.todo;

import com.heachi.admin.common.exception.ExceptionMessage;
import com.heachi.admin.common.exception.group.member.GroupMemberException;
import com.heachi.admin.common.exception.housework.HouseworkException;
import com.heachi.admin.common.utils.CachingStrategy;
import com.heachi.admin.common.utils.DayOfWeekUtils;
import com.heachi.housework.api.service.housework.todo.request.TodoSelectRequest;
import com.heachi.housework.api.service.housework.todo.request.VerifyTodoServiceRequest;
import com.heachi.housework.api.service.housework.todo.response.TodoResponse;
import com.heachi.mysql.define.group.member.GroupMember;
import com.heachi.mysql.define.group.member.repository.GroupMemberRepository;
Expand Down Expand Up @@ -87,6 +91,25 @@ public List<TodoResponse> selectTodo(TodoSelectRequest request) {
.collect(Collectors.toList());
}

@Transactional
public void verifyTodo(VerifyTodoServiceRequest request) {
// groupMemberId 가져오기
GroupMember groupMember = groupMemberRepository.findGroupMemberByUserEmailAndTodoId(request.getEmail(), request.getTodoId()).orElseThrow(() -> {
log.warn(">>>> 매칭되는 groupMember를 찾지 못했습니다.");

throw new GroupMemberException(ExceptionMessage.GROUP_MEMBER_NOT_FOUND);
});

HouseworkTodo houseworkTodo = houseworkTodoRepository.findHouseworkTodoByIdAndGroupMemberId(request.getTodoId(), groupMember.getId()).orElseThrow(() -> {
log.warn(">>>> 해당 그룹멤버는 집안일의 담당자가 아닙니다.");

throw new HouseworkException(ExceptionMessage.HOUSEWORK_TODO_PERMISSION_DENIED);
});

// 담당자가 맞다면, 업데이트
houseworkTodo.verifyHousework(request.getVerifyImageURL(), groupMember.getId());
}

private TodoList caching(TodoSelectRequest req, List<TodoResponse> todoResponseList) {

return todoListRepository.save(TodoList.builder() // List<TodoResponse> => TodoList 후 save
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.heachi.housework.api.service.housework.todo.request;

import lombok.Builder;
import lombok.Getter;

@Getter
public class VerifyTodoServiceRequest {
private Long todoId; // 집안일 아이디
private String email; // 인증 요청 이메일
private String verifyImageURL; // 인증 사진

@Builder
private VerifyTodoServiceRequest(Long todoId, String email, String verifyImageURL) {
this.todoId = todoId;
this.email = email;
this.verifyImageURL = verifyImageURL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ public class TodoResponse {
private LocalDate date;
private LocalTime endTime;
private String verificationPhotoURL;
private String verifierId;
private Long verifierId;
private LocalDateTime verificationTime;

@Builder
private TodoResponse(Long id, List<TodoUser> houseworkMembers, String category, String title, String detail,
Integer idx, HouseworkTodoStatus status, LocalDate date, LocalTime endTime,
String verificationPhotoURL, String verifierId, LocalDateTime verificationTime) {
String verificationPhotoURL, Long verifierId, LocalDateTime verificationTime) {
this.id = id;
this.houseworkMembers = houseworkMembers;
this.category = category;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.heachi.housework.TestConfig;
import com.heachi.housework.api.service.housework.todo.request.TodoSelectRequest;
import com.heachi.housework.api.service.housework.todo.request.VerifyTodoServiceRequest;
import com.heachi.housework.api.service.housework.todo.response.TodoResponse;
import com.heachi.housework.api.service.housework.todo.response.TodoUser;
import com.heachi.mysql.define.group.info.GroupInfo;
Expand All @@ -14,6 +15,7 @@
import com.heachi.mysql.define.housework.info.repository.HouseworkInfoRepository;
import com.heachi.mysql.define.housework.member.HouseworkMember;
import com.heachi.mysql.define.housework.member.repository.HouseworkMemberRepository;
import com.heachi.mysql.define.housework.todo.HouseworkTodo;
import com.heachi.mysql.define.housework.todo.constant.HouseworkTodoStatus;
import com.heachi.mysql.define.housework.todo.repository.HouseworkTodoRepository;
import com.heachi.mysql.define.user.User;
Expand Down Expand Up @@ -179,4 +181,49 @@ void test4() {
assertThat(result.getTodoList().size()).isEqualTo(2);
System.out.println("result = " + result);
}

@Test
@DisplayName("Todo 담당자가 사진을 업로드하면 정상적으로 사진이 저장된다.")
void test5() {
// given
User user = userRepository.save(generateUser());
User user2 = userRepository.save(generateCustomUser("[email protected]", "010-1111-1111"));
User user3 = userRepository.save(generateCustomUser("[email protected]", "010-2222-2222"));
GroupInfo groupInfo = groupInfoRepository.save(generateGroupInfo(user));
GroupInfo groupInfo2 = groupInfoRepository.save(generateGroupInfo(user2));
GroupInfo groupInfo3 = groupInfoRepository.save(generateGroupInfo(user3));
GroupMember groupMember = groupMemberRepository.save(generateGroupMember(user, groupInfo));
groupMemberRepository.save(generateGroupMember(user2, groupInfo));
groupMemberRepository.save(generateGroupMember(user3, groupInfo));
groupMemberRepository.save(generateGroupMember(user, groupInfo3));
groupMemberRepository.save(generateGroupMember(user3, groupInfo2));

HouseworkCategory houseworkCategory = houseworkCategoryRepository.save(generateHouseworkCategory());
HouseworkInfo houseworkInfo = houseworkInfoRepository.save(generateHouseworkInfo(houseworkCategory));
HouseworkInfo houseworkInfo2 = houseworkInfoRepository.save(generateHouseworkInfo(houseworkCategory));
HouseworkInfo houseworkInfo3 = houseworkInfoRepository.save(generateHouseworkInfo(houseworkCategory));

HouseworkMember houseworkMember = houseworkMemberRepository.save(generateHouseworkMember(groupMember, houseworkInfo));
HouseworkInfo findHouseworkInfo = houseworkInfoRepository.findHouseworkInfoByIdJoinFetchHouseworkMembers(houseworkInfo.getId()).get();
HouseworkInfo findHouseworkInfo2 = houseworkInfoRepository.findHouseworkInfoByIdJoinFetchHouseworkMembers(houseworkInfo2.getId()).get();
HouseworkInfo findHouseworkInfo3 = houseworkInfoRepository.findHouseworkInfoByIdJoinFetchHouseworkMembers(houseworkInfo3.getId()).get();

HouseworkTodo houseworkTodo = houseworkTodoRepository.save(generateHouseworkTodo(findHouseworkInfo, groupInfo, LocalDate.now()));
HouseworkTodo houseworkTodo2 = houseworkTodoRepository.save(generateHouseworkTodo(findHouseworkInfo2, groupInfo2, LocalDate.now()));
HouseworkTodo houseworkTodo3 = houseworkTodoRepository.save(generateHouseworkTodo(findHouseworkInfo3, groupInfo3, LocalDate.now()));

String uploadedImageURL = "abcd.sfsa";

// when
todoService.verifyTodo(VerifyTodoServiceRequest.builder()
.verifyImageURL(uploadedImageURL)
.todoId(houseworkTodo.getId())
.email(user.getEmail())
.build());
HouseworkTodo findHouseworkTodo = houseworkTodoRepository.findById(houseworkTodo.getId()).get();

// then
assertThat(findHouseworkTodo.getVerificationPhotoURL()).isEqualTo(uploadedImageURL);
assertThat(findHouseworkTodo.getVerificationTime()).isNotNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class QHouseworkTodo extends EntityPathBase<HouseworkTodo> {

public final DateTimePath<java.time.LocalDateTime> verificationTime = createDateTime("verificationTime", java.time.LocalDateTime.class);

public final StringPath verifierId = createString("verifierId");
public final NumberPath<Long> verifierId = createNumber("verifierId", Long.class);

public QHouseworkTodo(String variable) {
this(HouseworkTodo.class, forVariable(variable), INITS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public interface GroupMemberRepositoryCustom {
// 그룹 멤버의 ID 리스트에 해당하는 그룹멤버들을 조회한다. (그룹원인 경우만 = ACCEPT) - 집안일 추가시 담당자 지정을 위해 필요
public List<GroupMember> findGroupMemberListByGroupMemberIdList(List<Long> groupMemberIdList);

// Email과 todoId를 통해 GroupMember를 조회한다.
public Optional<GroupMember> findGroupMemberByUserEmailAndTodoId(String email, Long todoId);

// 그룹 멤버의 ID와 User 정보를 이용해 그룹 멤버를 조회한다.
public Optional<GroupMember> findGroupMemberByGroupMemberIdAndGroupInfoId(Long groupMemberId, Long groupId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static com.heachi.mysql.define.group.info.QGroupInfo.*;
import static com.heachi.mysql.define.group.member.QGroupMember.groupMember;
import static com.heachi.mysql.define.housework.todo.QHouseworkTodo.houseworkTodo;
import static com.heachi.mysql.define.user.QUser.user;

@Component
Expand Down Expand Up @@ -58,10 +59,25 @@ public List<GroupMember> findGroupMemberListByGroupMemberIdList(List<Long> group
.fetch();
}

@Override
public Optional<GroupMember> findGroupMemberByUserEmailAndTodoId(String email, Long todoId) {

return Optional.ofNullable(queryFactory
.selectFrom(groupMember)
.innerJoin(groupMember.user, user)
.where(groupMember.groupInfo.id.eq(
JPAExpressions.select(groupInfo.id)
.from(houseworkTodo)
.innerJoin(houseworkTodo.groupInfo, groupInfo)
.where(houseworkTodo.id.eq(todoId)))
.and(user.email.eq(email)))
.fetchOne());
}

@Override
public Optional<GroupMember> findGroupMemberByGroupMemberIdAndGroupInfoId(Long groupMemberId, Long groupId) {
// select gm from groupMember gm where gm.id= :groupMemberId and gm.groupInfo.id= :groupId
return Optional.of(queryFactory.selectFrom(groupMember)
return Optional.ofNullable(queryFactory.selectFrom(groupMember)
.innerJoin(groupMember.groupInfo, groupInfo).fetchJoin()
.where(groupMember.id.eq(groupMemberId)
.and(groupMember.groupInfo.id.eq(groupId)))
Expand All @@ -70,7 +86,7 @@ public Optional<GroupMember> findGroupMemberByGroupMemberIdAndGroupInfoId(Long g

@Override
public Optional<GroupMember> findGroupMemberByUserEmailAndGroupInfoId(String userEmail, Long groupId) {
return Optional.of(queryFactory.selectFrom(groupMember)
return Optional.ofNullable(queryFactory.selectFrom(groupMember)
.innerJoin(groupMember.user, user).fetchJoin()
.innerJoin(groupMember.groupInfo, groupInfo).fetchJoin()
.where(groupMember.user.email.eq(userEmail)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class HouseworkInfoRepositoryImpl implements HouseworkInfoRepositoryCusto
@Override
public Optional<HouseworkInfo> findHouseworkInfoByIdJoinFetchHouseworkMembers(Long houseworkInfoId) {

return Optional.of(queryFactory.selectFrom(houseworkInfo)
return Optional.ofNullable(queryFactory.selectFrom(houseworkInfo)
.leftJoin(houseworkInfo.houseworkMembers, houseworkMember).fetchJoin()
.innerJoin(houseworkInfo.houseworkCategory, houseworkCategory).fetchJoin()
.where(houseworkInfo.id.eq(houseworkInfoId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ public class HouseworkTodo extends BaseEntity {
private String verificationPhotoURL; // 다른 사용자가 확인할 수 있는 인증 사진 URL

@Column(name = "VERIFIER_ID")
private String verifierId; // 해당 집안일을 인증해준 사용자
private Long verifierId; // 해당 집안일을 인증한 사용자 (GroupMemberId)

@Column(name = "VERIFICATION_TIME")
private LocalDateTime verificationTime; // 인증된 시각

@Builder
private HouseworkTodo(HouseworkInfo houseworkInfo, GroupInfo groupInfo, String houseworkMember, String category,
String title, String detail, Integer idx, HouseworkTodoStatus status, LocalDate date,
LocalTime endTime, String verificationPhotoURL, String verifierId, LocalDateTime verificationTime) {
LocalTime endTime, String verificationPhotoURL, Long verifierId, LocalDateTime verificationTime) {
this.houseworkInfo = houseworkInfo;
this.groupInfo = groupInfo;
this.houseworkMember = houseworkMember;
Expand All @@ -93,6 +93,13 @@ private HouseworkTodo(HouseworkInfo houseworkInfo, GroupInfo groupInfo, String h
this.verificationTime = verificationTime;
}

// 집안일 인증
public void verifyHousework(String verificationPhotoURL, Long verifierId) {
this.verificationPhotoURL = verificationPhotoURL;
this.verifierId = verifierId;
this.verificationTime = LocalDateTime.now();
}

public static HouseworkTodo makeTodoReferInfo(HouseworkInfo houseworkInfo, GroupInfo groupInfo, LocalDate date) {

return HouseworkTodo.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.heachi.mysql.define.housework.todo.repository;

import com.heachi.mysql.define.housework.todo.HouseworkTodo;
import com.heachi.mysql.define.housework.todo.repository.response.HouseworkTodoCount;

import java.util.List;
import java.util.Optional;

public interface HouseworkTodoRepositoryCustom {

// groupInfoId 리스트를 받아 해당 그룹들의 오늘 날짜의 HouseworkTodo의 개수를 셀 수 있는 DTO를 반환하는 쿼리
public List<HouseworkTodoCount> findHouseworkTodoCountByGroupInfoIdList(List<Long> groupInfoIdList);

// todoId와 groupMemberId를 통해 해당 그룹원이 해당 집안일의 담당자인지 확인한다.
public Optional<HouseworkTodo> findHouseworkTodoByIdAndGroupMemberId(Long todoId, Long groupMemberId);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package com.heachi.mysql.define.housework.todo.repository;

import com.heachi.mysql.define.housework.todo.HouseworkTodo;
import com.heachi.mysql.define.housework.todo.repository.response.HouseworkTodoCount;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

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

import static com.heachi.mysql.define.group.info.QGroupInfo.groupInfo;
import static com.heachi.mysql.define.group.member.QGroupMember.groupMember;
import static com.heachi.mysql.define.housework.todo.QHouseworkTodo.houseworkTodo;
import static com.heachi.mysql.define.user.QUser.user;
import static com.querydsl.core.group.GroupBy.groupBy;
import static com.querydsl.core.group.GroupBy.list;

Expand All @@ -37,4 +42,20 @@ public List<HouseworkTodoCount> findHouseworkTodoCountByGroupInfoIdList(List<Lon
)
);
}

@Override
public Optional<HouseworkTodo> findHouseworkTodoByIdAndGroupMemberId(Long todoId, Long groupMemberId) {
HouseworkTodo findHouseworkTodo = queryFactory
.selectFrom(houseworkTodo)
.where(houseworkTodo.id.eq(todoId))
.fetchOne();

if (findHouseworkTodo != null && !findHouseworkTodo.getHouseworkMember().isEmpty()) {
return Arrays.stream(findHouseworkTodo.getHouseworkMember().split(","))
.map(Long::parseLong)
.anyMatch(gmId -> gmId.equals(groupMemberId)) ? Optional.of(findHouseworkTodo) : Optional.empty();
} else {
return Optional.empty();
}
}
}
Loading