diff --git a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberCommandUseCase.java b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberCommandUseCase.java index b9c10539..11f1bb18 100644 --- a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberCommandUseCase.java +++ b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberCommandUseCase.java @@ -11,6 +11,7 @@ import b1nd.dodam.restapi.support.data.Response; import b1nd.dodam.restapi.support.encrypt.Sha512PasswordEncoder; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -66,6 +67,7 @@ private void throwExceptionWhenMemberIsBroadcastClubMember(Member member) { } } + @CacheEvict(value = "members-cache", key = "'activeMembers'") public Response delete(String id) { Member member = getMemberById(id); throwExceptionWhenAuthStatusIsActive(member); @@ -79,16 +81,19 @@ private void throwExceptionWhenAuthStatusIsActive(Member member) { } } + @CacheEvict(value = "members-cache", key = "'activeMembers'") public Response active(String id) { updateStatus(id, ActiveStatus.ACTIVE); return Response.ok("멤버 활성화 성공"); } + @CacheEvict(value = "members-cache", key = "'activeMembers'") public Response deactivate(String id) { updateStatus(id, ActiveStatus.DEACTIVATE); return Response.ok("멤버 비활성화 성공"); } + @CacheEvict(value = "members-cache", key = "'activeMembers'") public Response deactivate() { Member member = memberAuthenticationHolder.current(); member.updateStatus(ActiveStatus.DEACTIVATE); @@ -113,6 +118,7 @@ public Response updatePassword(UpdatePasswordReq req) { return Response.noContent("비밀번호 수정 성공"); } + @CacheEvict(value = "members-cache", key = "'activeMembers'") public Response updateMemberInfo(UpdateMemberInfoReq req) { Member member = memberAuthenticationHolder.current(); member.updateInfo(req.name(), req.email(), req.phone(), req.profileImage()); @@ -120,6 +126,7 @@ public Response updateMemberInfo(UpdateMemberInfoReq req) { return Response.noContent("내 정보 수정 성공"); } + @CacheEvict(value = "members-cache", key = "'activeMembers'") public Response updateStudentInfo(UpdateStudentInfoReq req) { Student student = getStudentByMember(memberAuthenticationHolder.current()); student.updateInfo(req.grade(), req.room(), req.number()); @@ -131,4 +138,4 @@ private Student getStudentByMember(Member member) { .orElseThrow(StudentNotFoundException::new); } -} +} \ No newline at end of file diff --git a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberQueryUseCase.java b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberQueryUseCase.java index 11f87be9..7b04c333 100644 --- a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberQueryUseCase.java +++ b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/MemberQueryUseCase.java @@ -9,6 +9,7 @@ import b1nd.dodam.restapi.member.application.data.res.MemberInfoRes; import b1nd.dodam.restapi.support.data.ResponseData; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -42,6 +43,7 @@ public ResponseData> getDeactivateMembers() { .toList()); } + @Cacheable(value = "members-cache", key = "'activeMembers'") public ResponseData> getAll() { return ResponseData.ok("모든 멤버 정보 조회 성공", service.getByStatus(ActiveStatus.ACTIVE).parallelStream() .map(this::getMemberInfo) @@ -65,4 +67,4 @@ public ResponseData checkBroadcastClubMember(String id) { return ResponseData.ok("방송부원 확인 성공", service.checkBroadcastClubMember(member)); } -} +} \ No newline at end of file diff --git a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/data/res/MemberInfoRes.java b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/data/res/MemberInfoRes.java index ff81273f..78e6a0b8 100644 --- a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/data/res/MemberInfoRes.java +++ b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/member/application/data/res/MemberInfoRes.java @@ -5,6 +5,10 @@ import b1nd.dodam.domain.rds.member.entity.Teacher; import b1nd.dodam.domain.rds.member.enumeration.ActiveStatus; import b1nd.dodam.domain.rds.member.enumeration.MemberRole; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import java.time.LocalDateTime; @@ -18,7 +22,11 @@ public record MemberInfoRes( String phone, StudentRes student, TeacherRes teacher, + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) LocalDateTime createdAt, + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) LocalDateTime modifiedAt ) { public static MemberInfoRes of(Member member, Student student, Teacher teacher) { diff --git a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/support/data/ResponseData.java b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/support/data/ResponseData.java index 30d40a21..e1310b61 100644 --- a/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/support/data/ResponseData.java +++ b/dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/restapi/support/data/ResponseData.java @@ -1,9 +1,17 @@ package b1nd.dodam.restapi.support.data; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Getter; import org.springframework.http.HttpStatus; +import java.io.IOException; + @Getter +@JsonDeserialize(using = ResponseData.ResponseDataDeserializer.class) public class ResponseData extends Response { private final T data; @@ -25,4 +33,17 @@ public static ResponseData created(String message, T data) { return new ResponseData<>(HttpStatus.CREATED, message, data); } + public static class ResponseDataDeserializer extends JsonDeserializer { + @Override + public ResponseData deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.getCodec().readTree(p); + HttpStatus status = HttpStatus.valueOf(node.get("status").asInt()); + String message = node.get("message").asText(); + JsonNode dataNode = node.get("data"); + Object data = p.getCodec().treeToValue(dataNode, Object.class); + return new ResponseData<>(status, message, data); + } + } + } + diff --git a/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/repository/MemberRepository.java b/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/repository/MemberRepository.java index 32f93cc2..464c92c2 100644 --- a/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/repository/MemberRepository.java +++ b/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/repository/MemberRepository.java @@ -3,6 +3,7 @@ import b1nd.dodam.domain.rds.member.entity.Member; import b1nd.dodam.domain.rds.member.enumeration.ActiveStatus; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.time.LocalDateTime; import java.util.List; @@ -16,7 +17,12 @@ public interface MemberRepository extends JpaRepository { List findByCreatedAtAfter(LocalDateTime createdAt); - List findByStatus(ActiveStatus status); + @Query("SELECT m FROM member m LEFT JOIN student s ON m.id = s.member.id " + + "WHERE m.status = :status " + + "ORDER BY CASE WHEN s.id IS NOT NULL THEN 0 ELSE 1 END ASC, " + + "s.grade ASC, s.room ASC, s.number ASC") + List findByStatusOrderByStudent(ActiveStatus status); + List findByNameContains(String name); diff --git a/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/service/MemberService.java b/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/service/MemberService.java index 33b2e8e3..76137fb8 100644 --- a/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/service/MemberService.java +++ b/dodam-system-domain/dodam-domain-rds/src/main/java/b1nd/dodam/domain/rds/member/service/MemberService.java @@ -73,7 +73,7 @@ public List searchByName(String name) { } public List getByStatus(ActiveStatus status) { - return memberRepository.findByStatus(status); + return memberRepository.findByStatusOrderByStudent(status); } public Optional getStudentByMember(Member member) { diff --git a/dodam-system-domain/dodam-domain-redis/src/main/java/b1nd/dodam/domain/redis/support/config/RedisConfig.java b/dodam-system-domain/dodam-domain-redis/src/main/java/b1nd/dodam/domain/redis/support/config/RedisConfig.java index b29fa655..b4361b7c 100644 --- a/dodam-system-domain/dodam-domain-redis/src/main/java/b1nd/dodam/domain/redis/support/config/RedisConfig.java +++ b/dodam-system-domain/dodam-domain-redis/src/main/java/b1nd/dodam/domain/redis/support/config/RedisConfig.java @@ -58,6 +58,8 @@ public RedisCacheManager redisCacheManager() { cacheConfigurations.put("meal-month", RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(MONTH_EXPIRE_SECONDS))); + cacheConfigurations.put("members-cache", RedisCacheConfiguration.defaultCacheConfig()); + return RedisCacheManager.RedisCacheManagerBuilder .fromConnectionFactory(redisConnectionFactory()) .cacheDefaults(redisCacheConfiguration)