Skip to content

Commit

Permalink
[이성 목록 조회] 이성 목록을 조건에 따라 조회할 수 있다(#43) (#83)
Browse files Browse the repository at this point in the history
* feat: 화살 보내기 요청 DTO 추가(#24)

보낼 화살 수를 담은 요청 DTO 정의

* feat: 확살 송수신 내역 repository, 내역 확인 로직 추가(#24)

- 화살 송수신 내역 repository 정의
- 보낸 사용자, 받은 사용자를 통해 내역 존재 여부를 확인하는 로직 구현

* feat: 화살 송수신 내역 존재 여부 확인 로직 추가(#24)

보낸 사용자, 받은 사용자를 통해 송수신 내역 존재 여부를 확인하는
로직 구현

* feat: 화살 송수신 내역 저장 기능 구현(#24)

* feat: 화살 관련 예외 enum 정의(#24)

다음 상황들에 대한 예외 enum을 정의하였다.
- 자기 자신에게 화살을 보내는 경우
- 이미 화살을 보낸 사용자인 경우
- 가진 화살 수가 부족해 화살을 보낼 수 없는 경우

* feat: 사용자 화살 감소 로직 추가(#24)

감소하려는 화살 수가 사용자가 보유한 화살 수보다 클 경우 예외 발생

* refactor: 생성 시간 필드, 불필요한 업데이트 불가 DDL 속성 삭제(#24)

* feat: 화살 보내기 비즈니스 로직 추가(#24)

화살 보내기 비즈니스 로직은 다음 과정을 거친다.
1. 자기 자신에게 화살을 보내는 상황 검증
2. 화살을 보낸 사용자에게 또 보내는 상황 검증
3. 화살 내역 저장
4. 보내는 사용자 화살 감소, 화살이 부족할 경우 예외 발생(과정 3 롤백)
5. 받는 사용자 화살 증가

* feat: 화살 보내기 API 구현(#24)

* refactor: 화살 command repository 삭제(#24)

- query, command를 service, repository를 중개하는 별도의 계층을 통해 구분
- 이에 따라 기존 command repository 삭제
- 기존 command repository 사용 위치, 화살 command로 대체

* refactor: 코드 정렬 수정(#24)

* refactor: 사용자 화살 증감 메서드 이름 수정(#24)

서로 대치되는 작업을 수행하는 상황을 잘 표현할 수 있도록 메서드 이름 수정

* feat: 화살 관련 예외 enum 추가(#24)

- 이미 화살을 보낸 사용자 예외
- 화살이 부족하여 화살을 보낼 수 없는 예외

* refactor: 화살 내역 존재 여부 확인 메서드 이름 수정(#24)

가독성 향상을 위해 간단한 이름을 수정

* refactor: 코드 배치 수정(#24)

dev 브랜치 작업 내역 병합에 따른 코드 배치 수정

* refactor: 화살 보내기 API 파라미터 이름 수정(#24)

좀 더 명확하게 대치되는 의미를 나타낼 수 있도록 화살을 받는
사용자 ID 파라미터 이름을 receiverId로 수정

* refactor: 컨벤션에 맞게 코드 배치 수정(#24)

* feat: 이름 칼럼, 생성자 추가(#27)

- 이름 칼럼 추가
- 테스트시 활용 가능한 생성자 추가

* feat: 상대 프로필 응답 DTO 수정(#27)

- 래퍼 타입 필드들 기본 타입으로 변경
- 프로필 사진 URL 리스트 필드 추가
- 사용자가 가진 총 화살 수 필드 추가
- 음주 성향, 흡연 성향 필드 추가
- 코드 정렬

* feat: 사용자 엔티티 수정(#27)

- 레퍼런스 타입 not null 필드들, Column 어노테이션 사용 명시
- salt 필드 추가
- 생성 일시, 최종 수정 일시 필드 추가
- JpaAuditing 활용, 생성/최종 수정 일시 필드들 엔티티 저장시 자동 설정
- 새로운 생성자 구성

* feat: 상대 프로필 조회 비즈니스 로직 추가(#27)

프로필을 조회하는 사용자 ID의 경우 상대방이 화살을 보넀던 사용자인지
확인하기 위해 파라미터로 받는다.

* feat: 상대 프로필 API 구현 및 코드 정렬(#27)

* feat: 사용자 엔티티 수정(#27)

- 요구사항 변경에 따라 음주 성향, 흡연 성향 필드 삭제
- 성별 정보 제공 메서드 추가

* feat: 사용자 프로필 응답 DTO 수정(#27)

- 음주 성향, 흡연 성향 필드 삭제
- 성별 필드 추가

* feat: 사용자 프로필 상세 응답 DTO 수정(#27)

- 음주 성향, 흡연 성향 필드 삭제
- 생성자 성별 필드 설정 로직 수정
- 코드 정렬

* feat: 사용자 프로필 조회 비즈니스 로직 수정(#27)

- 음주 성향, 흡연 성향 삭제
- 성별 정보 응답에 포함토록 수정
- 코드 정렬

* chore: spring mail 종속성 추가(#35)

* chore: Gmail SMTP 사용을 위한 설정 추가(#35)

* feat: 이메일 기반 사용자 조회 로직 추가(#35)

* feat: 이메일 기반 사용자 조회 로직 추가(#35)

* feat: 서비스 사용 정규식 enum 정의(#35)

- 서비스에서 사용하는 정규식들을 모아놓는 enum 클래스 정의
- 이메일, 비밀번호 정규식 정의

* feat: 임시 비밀번호 발급 기능 구현(#35)

- 비밀번호 정규식을 충족하는 임의의 임시 비밀번호를 발급
- Random보다 생성하는 난수를 예측하기 어려워 보안적으로 뛰어난 SecureRandom 사용

* feat: 메일 관련 설정 정의(#35)

- spring mail에서 이메일 전송을 위해 제공하는 JavaMailSender 빈 등록
- 필요한 설정 값들은 application.yml에 정의된 값들을 끌어와 사용
- 빈 등록 로직에서 구글 SMTP 서버를 통해 메일을 전송하기 위한 설정 코드 추가

* feat: 이메일 전송 로직 추가(#35)

- 단순 텍스트 이메일 전송 기능 구현
- 발신자는 application.yml에 정의해논 구글 SMTP 사용자 이름 값으로 설정

* feat: 임시 비밀번호 발급 비즈니스 로직 추가(#35)

임시 비밀번호는 다음 과정을 거친다.
1. 요청 이메일 기반 사용자 조회
2. 임시 비밀번호 생성
3. 사용자 비밀번호, 임시 비밀번호로 변경
4. 임시 비밀번호 발급 메일 전송
임시 비밀번호를 암호화하는 로직은 추후 구현 예정

* feat: 임시 비밃번호 발급 비즈니스 로직 트랜잭션 정의 추가(#35)

* feat: 임시 비밀번호 발급 요청 DTO 수정(#35)

- 불필요한 기존 생성자 삭제, 기본 생성자 추가
- 비밀번호 형식 제약 조건 추가
- 코드 정렬

* feat: 임시 비밀번호 발급 API 구현(#35)

- 임시 비밀번호 발급 API 구현
- 명세 속성 수정
- 코드 정렬

* refactor: 병합에 따른 코드 재배치(#35)

dev 브랜치 작업 내역 병합에 따른 코드 재배치

* refactor: 병합에 따른 코드 재배치(#35)

dev 브랜치 작업 내역 병합에 따른 코드 재배치

* chore: sms 서비스 sdk 의존성 추가(#28)

coolsms 서비스에서 제공하는 java sdk 의존성 추가

* chore: sms 서비스 관련 설정 추가(#28)

sms 서비스를 이용하기 위해 필요한 설정들 추가

* feat: sms 서비스 이용을 위한 설정 정의(#28)

- coolsms에서는 메시지 전송을 수행할 수 있는 DefaultMessageService 제공
- config에서 application.yml을 통해 값을 주입 받아 DefaultMessageService 빈 등록

* feat: 인증 코드 생성 로직 추가(#28)

- apache.common.lang3 라이브러리의 RandomStringUtils 이용
- 영어 대소문자, 숫자로 구성된 6자리 랜덤 인증 코드를 생성
- 영어 대소문자 52개(26+26), 숫자 10개로 총 62개의 선택 가능 문자 존재
- 생성 가능한 인증 코드 수, 약 56억 8천만 개(62^6)로 보안성을 확보하였음

* feat: 문자 전송 로직 추가(#28)

* feat: 전화번호 기반 사용자 존재 여부 확인 로직 정의(#28)

* feat: 전화번호 기반 사용자 존재 여부 확인 로직 추가(#28)

* feat: 전화번호 형식 제약 조건 추가(#28)

010으로 시작하며 0~9까지의 숫자로 이뤄진 11자리 문자열만 허용토록 제약 조건 추가

* chore: application.yml 수정(#28)

sms.provider 속성도 application.properties 통해 관리하도록 수정

* feat: 인증 코드 sms 전송 비즈니스 로직 추가(#28)

- 요청된 전화번호를 통해 사용자 존재 확인
- 사용자가 존재할 경우만 sms로 인증 코드 전송

* feat: 전화번호 인증 코드 전송 API 구현(#28)

* chore: spring data redis 의존성 추가(#28)

* chore: redis 관련 설정 추가(#28)

* feat: redis 관련 설정 정의(#28)

- redis 커넥션 팩토리 빈 등록
- CRUD 연산 지원을 위한 RedisTemplate 빈 등록

* feat: redis 키-값 저장,조회,삭제 로직 추가(#28)

- redis를 이용한 저장, 삭제 기능 지원 유틸 클래스 정의
- 만료 시간을 분 단위로 설정하는 저장 로직 구현
- 기본 조회 로직 구현, Optional 리턴 타입을 통해 null 처리 지원
- 기본 삭제 로직 구현

* feat: 인증 코드 sms 전송 비즈니스 로직 수정(#28)

- 인증 코드 sms 전송 이전 redis에 번호를 키로 코드를 저장하는 로직 추가
- 새 인증 코드 발급 이전 기존 발급 내역 삭제

* feat: 인증 코드 정규식 정의(#28)

영어 대소문자, 숫자로 이뤄진 6자리 문자열만 허용하는 정규식 enum 정의

* feat: 아이디 찾기 예외 enum 정의(#28)

- 인증 코드 내역이 존재하지 않는 경우(인증 코드 만료도 포함)
- 인증 코드가 일치하지 않는 경우

* feat: 전화번호 기반 사용자 조회 기능 정의(#28)

* feat: 전화번호 기반 사용자 조회 로직 추가(#28)

* refactor: 코드 정렬(#28)

* feat: 아이디 찾기 요청 DTO 수정(#28)

- 인증 번호를 발급한 전화번호 필드 추가
- 인증 코드 필드 명세 속성 수정
- 인증 코드 정규식 제약 조건 추가
- 불필요한 기존 생성자, 기본 생성자로 대체

* feat: 아이디 찾기 비즈니스 로직 추가(#28)

아이디 찾기 요청은 인증 코드 발급 전화번호, 인증 코드를 포함한다.
아이디 찾기는 다음 과정을 거쳐 이뤄진다.
1. 발급 전화번호를 기반으로 인증 코드 redis에서 조회
2. 인증 코드 일치 확인
3. 전화번호 기반 사용자 조회
4. 확인한 인증 코드 삭제
5. 찾은 이메일을 응답으로 반환

* feat: 아이디 찾기 API 구현(#28)

* feat: 전화번호 정규식 정의(#28)

* feat: 회원가입 요청 DTO 수정(#8)

- 요구사항 변경에 따라 필드들 삭제 및 추가
- 필드들 API 명세
- 문자열 필드들 정규식 제약 조건 추가
- 불필요한 생성자 삭제, 기본 생성자로 대체

* refactor: PasswordEncryptor 최상위 util 패키지로 이동(#8)

- 비밀번호 암호화 기능을 전역적으로 사용하기에 패키지 경로 변경
- 사용 위치 수정

* refactor: 잘못된 들여쓰기 수정을 위해 코드 정렬(#8)

* feat: 이름 기반 동물상 조회 로직 정의(#8)

* feat: 이름 기반 닮은 동물상 조회 로직 구현(#8)

* feat: 이름 기반 지역 조회 로직 정의(#8)

* feat: 이름 기반 지역 조회 로직 구현(#8)

* feat: 분류 기반 음역대 조회 로직 정의(#8)

* feat: 분류 기반 음역대 조회 로직 구현(#8)

* feat: 사용자 선호 조건 repository 정의(#8)

* feat: 사용자 선호 조건 엔티티 저장 로직 구현(#8)

* feat: 사용자 선호조건 엔티티 구성(#8)

이성 추천, 다른 사용자 검색시 사용되는 사용자 선호조건 엔티티 정의
다음 필드들을 포함한다.
- 선호하는 나이 하한, 상한
- 선호하는 키 하한, 상한
- 선호하는 MBTI
- 선호하는 체형
- 선호하는 음역대
- 선호하는 지역
- 선호하는 동물상
- 선호 조건 소유 사용자

* feat: 사용자 엔티티 저장 로직 구성(#8)

* feat: 프로필 사진 repository 정의(#8)

* feat: 프로필 사진 엔티티 저장 로직 구성(#8)

* feat: 회원가입 관련 예외 enum 정의(#8)

다음 예외 enum 들을 정의
- 비밀번호와 비밀번호 확인 값 불일치 상황
- 존재하지 않는 음역대
- 존재하지 않는 동물상
- 존재하지 않는 지역

* refactor: 분류 기반 음역대 조회 로직 수정(#8)

- 잘못된 인덴트 수정, 코드 정렬
- 회원가입 관련 예외 enum을 사용토록 로직 수정

* refactor: 이름 기반 닮은 동물상 조회 기능 수정(#8)

- 잘못된 들여쓰기 수정, 코드 정렬
- 회원가입 관련 예외 enum을 이용하도록 수정

* refactor: 이름 기반 지역 조회 로직 수정(#8)

- 잘못된 들여쓰기, 코드 정렬 수정
- 회원가입 관련 예외 enum 사용토록 수정

* refactor: 잘못된 들여쓰기, 코드 정렬로 수정(#8)

* refactor: 회원가입 요청 DTO 수정(#8)

- 기존 생성자 삭제, @ModelAttribute 사용을 위한 생성자 추가
- 잘못된 들여쓰기 수정을 위한 코드 정렬 수정

* feat: 회원가입 응답 DTO 구성(#8)

회원가입시 바로 서비스를 이용할 수 있도록 access token과
refresh token을 바로 지급하는 형태로 구성

* feat: 성별 enum 구성(#8)

- 성별 enum 정의
- 문자열 성별 값을 enum으로 치환할 수 있는 편의 메서드 구현

* feat: 비밀번호 암호화시 사용되는 salt 생성 로직 구현(#8)

* refactor: 잘못된 들여쓰기 수정, 코드 정렬(#8)

* feat: 회원가입 관련 로직 추가(#8)

- 사용자 엔티티 생성 및 저장 로직
- 프로필 사진 엔티티 생성 및 저장 로직
- 사용자 선호조건 엔티티 생성 및 저장 로직

* feat: 회원가입 비즈니스 로직 초안 구성(#8)

회원가입 비즈니스 로직은 다음 과정을 거쳐 이뤄진다.
1. 사용자 엔티티 생성 및 저장
2. 프로필 사진 엔티티 생성 및 저장
3. 사용자 선호조건 엔티티 생성 및 저장
4. access token, refresh token 발급

* feat: 회원가입 API 초안 구현 및 코드 정렬(#8)

- 회원가입 API 구현
- 잘못된 들여쓰기 수정을 위한 코드 정렬

* refactor: 분류 기반 음역대 조회 로직 이름 수정, 코드 정렬(#8)

* feat: 프로필 사진 엔티티 저장 로직 수정(#8)

무조건 일괄적 List 형태로 저장되는 특성을 고려해 List 파라미터로 받아
일괄 저장하도록 로직 수정

* feat: S3 이용 프로필 사진 업로드 로직 추가(#8)

프로필 사진 업로드는 다음 과정을 거쳐 이뤄진다.
1. 사진 파일들 확장자 검증
2. 파일 식별을 위한 키 생성, profilephoto/{userId}-{photo idx} 형식
3. 사진 파일 업로드 요청 생성 및 업로드 수행

* feat: 프로필 사진 엔티티 저장 로직에 S3 업로드 로직 추가(#8)

* feat: 프로필 사진 컬렉션 필드 초기화 & 연관관계 설정 로직 추가(#8)

- NPE 방지를 위한 컬렉션 필드 초기화 설정
- 순수한 객체 관계를 고려한 프로필 사진 엔티티 연관관계 설정 로직 구성

* feat: 이미 사용 중인 닉네임, 이메일 확인 로직 정의(#8)

- 이미 사용중인 닉네임 확인 로직 정의
- 이미 사용중인 이메일 확인 로직 정의

* feat: 이미 사용 중인 닉네임, 이메일 확인 로직 추가(#8)

* feat: 랜덤 salt 생성 로직 수정(#8)

- salt의 길이 고정 32비트(4바이트)로 변경
- 32비트 길이 salt 만으로 대부분의 보안적 위협 충분히 커버 가능
- base64 인코딩에 따라 로직에서 생성되는 salt 문자열의 길이는 8

* feat: 이미 사용 중인 이메일/닉네임 예외 추가(#8)

- 이미 사용 중인 이메일 예외 enum 정의
- 이미 사용 중인 닉네임 예외 enum 정의

* feat: s3 업로드된 프로필 사진 삭제 로직 추가(#8)

* feat: 회원가입 관련 로직 수정(#8)

- 사용자 엔티티 저장 로직에 이미 사용 중인 이메일/닉네임 검증 로직 추가
- 프로필 사진 저장 로직에 사용자 엔티티와의 연관관계 설정 로직 추가

* feat: 프로필 사진 삭제 이벤트 정의(#8)

- 회원가입 과정중 트랜잭션 롤백 발생시 업로드된 프로필 사진 삭제를 위해 정의
- 업로드된 프로필 사진 엔티티를 필드를 통해 전달

* feat: 프로필 사진 삭제 이벤트 리스너 정의(#8)

- 회원가입 과정중 트랜잭션 롤백 발생시 프로필 사진 삭제 이벤트 발생
- 이벤트를 통해 삭제 대상인 프로필 사진 엔티티들을 전달 받음
- S3 서비스를 통해 해당 엔티티들의 삭제 작업 진행

* feat: 회원가입 로직 롤백시 업로드된 프로필 사진 삭제 이벤트 발행 로직 추가(#8)

회원가입 도중 예외 상황 등으로 인해 트랜잭션 롤백시 이미 s3에 업로드된 프로필
사진들을 삭제하기 위한 이벤트 발행 로직 추가

* chore: jjwt 의존성 추가(#8)

jwt 활용을 위해 사용되는 jjwt 라이브러리 의존성 추가

* feat: access/refresh token 발급 및 검증 로직 추가(#8)

- jwt 시크릿 키 값의 경우 무작위 64바이트 바이너리를 16진수로 인코딩한 문자열 사용
- jwt 포맷의 access/refresh token 발급 로직 추가
- 만료 시간을 계산하는 공통 로직, 별도의 메서드로 추출
- 사용자 엔티티를 기반으로 jwt를 생성하는 공통 로직, 별도의 메서드로 추출
- access token의 보편적인 존속기간은 수 시간가량 따라서 8시간으로 설정
- refresh token의 보편적인 존속기간은 수 일에서 수 개월 내외 따라서 10일로 설정
- jwt의 일반적인 관행에 따라 사용자 이메일을 subject로 설정
- 사용자 엔티티의 PK를 클레임으로 설정하여 인가시 사용할 수 있도록 구성
- 검증 로직에선 시그니처의 유효성과 JWT 만료 여부를 검증

* feat: refresh token 필드 및 업데이트 로직 추가(#8)

- 사용자의 refresh token을 저장할 필드 추가
- 사용자 refresh token 업데이트 로직 추가

* feat: 회원가입 로직, access/refresh token 발급 로직 추가(#8)

- 회원가입 정상 완료의 결과로 access token과 refresh token 제공
- 회원가입 비즈니스 로직에 access token, refresh token 발급 로직 추가
- 새 refresh token 발급시 사용자 refresh token 업데이트 수행

* refactor: 잘못된 주석 들여쓰기 수정(#8)

* refactor: 문자열 값 Geneder enum 변환 메서드 이름 수정(#8)

* refactor: 회원가입 비즈니스 로직 코드 정렬(#8)

* refactor: Gender 문자열 값 변환 메서드 사용 위치 수정 및 코드 정렬(#8)

* feat: salt 길이 32바이트로 변경(#8)

보안적으로 안전한 32자로 salt 길이 변경

* refactor: 병합으로 코드 수정(#8)

* feat: 임시 비밀번호 발급 로직에 비밀번호 암호화 로직 추가(#8)

* refactor: 잘못된 들여쓰기 간격 수정(#8)

* chore: querydsl 의존성 추가(#43)

* feat: querydsl 관련 설정 정의(#43)

* feat: 즐겨찾기한 사용자 목록 조회 로직 정의(#43)

* feat: 즐겨찾기한 사용자 조회 로직 추가(#43)

- input으로 들어오는 페이지 번호(page)가 0부터 시작한다 가정
- 페이지 형태로 조회 결과 제공
- 조회 쿼리와 카운트 쿼리 분리, 공통 쿼리를 별도 메서드로 추출
- 조회 성능 향상을 위해 Projections 활용하여 DTO로 조회
- 즐겨찾기한 사용자 조회시 삭제되거나 휴면 상태인 사용자 배제

* feat: querydsl 커스텀 repository 상속 추가(#43)

* refactor: 사용자 목록 조회 요청 DTO 수정(#43)

- includePreferredAnimal 필드, nullable하기에 wrapper 타입으로 변경
- 검증 조건 예외 메시지 추가
- 코드 재정렬

* feat: 사용자 목록 조회 응답 DTO 필드 추가 및 수정(#43)

- age 필드 추가
- not null 필드들 원시 타입으로 변경
- is... 네이밍의 boolean 타입 필드, 이름 그대로 직렬화 되지 않는 문제 해결을 위해 wrapper 타입으로 설정

* refactor: 사용자 목록 조회 페이지 응답 DTO 수정(#43)

- not null 필드들 원시 타입으로 변경
- is... 네이밍 필드 이름 그대로 직렬화되기 위해 wrapper 타입으로 설정
- 코드 정렬

* feat: 즐겨찾기한 사용자 조회 로직 추가(#43)

* feat: 즐겨찾기한 사용자 조회 비즈니스 로직 추가(#43)

* feat: 이성 목록 조회 API, 즐겨찾기한 사용자 조회 로직 추가(#43)

* refactor: 부적절한 쿼리 생성 메서드명 수정(#43)

* refactor: 화살 보낸 사용자, 화살 받은 사용자 필드명 수정(#43)

- 화살 보낸 사용자 sender로 이름 수정
- 화살 받은 사용자 receiver로 이름 수정
- 관련 로직에서 변수명, 메서드명 수정

* feat: 화살 보낸 사용자 조회 로직 정의(#43)

* refactor: type 필드 API 명세 내용 수정(#43)

화살 보낸 사용자 조회시 type = ARROW_RECEIVERS로 요청하도록 수정

* feat: 편의성 팩토리 메서드 추가(#43)

Page를 파라미터로 받아 UserQueryPageResponse를 생성하는 정적 팩토리 메서드 추가

* refactor: 지인 엔티티 수정(#43)

- 사용자 엔티티와의 연관관계 형성
- not null 필드들 칼럼 속성 추가
- 생성자 수정

* feat: 화살 보낸 사용자 조회 로직 추가 및 일부 로직 수정(#43)

- 화살을 보낸 사용자 조회 로직 추가
    화실을 보낸 사용자를 조회할 때 기본적으로 다음 사용자를 배제한다.
    - 휴면 상태인 사용자
    - 삭제된 사용자
    - 지인
- 공통적으로 사용되는 where 파라미터절에 오는 조건, 서브 쿼리 별도 메서드 추출
- 카운트 쿼리에서 불필요한 join 절 제거

* feat: 화살을 보낸 사용자 조회 로직 추가(#43)

* feat: 화살을 보낸 사용자 조회 비즈니스 로직 추가(#43)

* feat: 사용자 목록 조회 API, 화살 보낸 사용자 조회 로직 추가(#43)

* chore: 잘못된 TODO 수정(#43)

* feat: 나에게 화살을 보낸 사용자 조회 로직 정의(#43)

* feat: 나에게 화살을 보낸 사용자 조회 로직 추가 및 일부 로직 수정(#43)

- 나에게 화살을 보낸 사용자 조회 로직 추가
  휴면 상태인 사용자, 삭제된 사용자를 배제하고 조회
- 지인은 애초에 즐겨찾기 등록, 화살 보내기가 불가능하므로 연관 조회 로직에서
  지인을 제외하는 조건 삭제
- 일부 잘못된 로직 및 주석 내용 수정

* feat: 나에게 화살을 보낸 사용자 조회 로직 추가(#43)

* feat: 나에게 화살을 보낸 사용자 조회 비즈니스 로직 추가, 일부 로직 수정(#43)

- 공통적으로 사용되는 기본 페이지 사이즈(6) 상수 정의
- 나에게 화살을 보낸 사용자 조회 비즈니스 로직 추가

* feat: 사용자 목록 조회 API,내게 화살을 보낸 사용자 조회 로직 추가(#43)

* feat: 사용자 검색 내역 엔티티 추가(#43)

* feat: 사용자 추천 내역 repository 정의(#43)

* feat: 사용자 추천 내역 저장 로직 구현(#43)

* feat: 사용자(이성) 추천 로직 정의(#43)

* feat: 사용자 목록 조회 요청 DTO 수정(#43)

- 불필요한 필드 제거
- type 필드 정규식 검증 조건 추가

* feat: 사용자 추천 내역 엔티티 수정(#43)

- 엔티티 리스너 활용, 엔티티 저장시 createdAt 필드 자동 설정
- 기존 생성자 제거, 적절한 새 생성자 구성

* feat: 사용자 추천 내역 엔티티 저장 로직 구성(#43)

사용자 추천 내역은 한 번에 2개씩 저장되기 때문에 기존 메서드를 제거하고
컬렉션 형태로 엔티티를 저장하는 로직 구성

* feat: 추천 이성 조회 로직 추가 및 리팩터링(#43)

- 추천 이성 조회 로직 추가
    이성 추천시 배제되는 사용자
        - 자기 자신(조회하는 사용자)
        - 추천(선호) 조건을 만족하지 않는 사용자
        - 화살을 주고 받은 적이 있는 사용자
        - 즐겨찾기한 사용자
        - 삭제, 휴면 상태인 사용자
        - 지인(전화번호로 구분)
        - 추천 일자에 이미 추천된 사용자
        - 추천 일자에 검색된 적 있는 사용자
- UserQueryResponse에 필요한 필드를 select 하는 쿼리, 별도 메서드 추출
- exists를 통해 특정 데이터 존재 여부를 확인하던 로직들,
  selectOne 형식으로 변경. 이를 통해 존재하는 경우와
  존재하지 않는 경우에 모두 활용 가능

* feat: 추천 이성 조회 로직 추가(#43)

- 추천 이성 조회 로직 추가
- 여러 Id를 기반으로 다수의 사용자를 조회하는 로직 추가

* feat: 추천 이성 조회 비즈니스 로직 추가 및 리팩터링(#43)

- 추천 이성 조회 비즈니스 로직 추가
- 페이지 사이즈가 같은 조회 로직들, 통합된 한 로직내에서 type으로
  분기하여 처리토록 리팩터링

* feat: 사용자 목록 조회 API, 추천 이성 조회 로직 추가(#43)

- 추천 이성 조회 로직 추가
- 이성 추천의 경우를 제외한 나머지 한 비즈니스 로직으로 통합하여 처리

* refactor: 병합에 따른 코드 수정(#43)

dev 브랜치 작업내역 병합에 따른 코드 수정

* refactor: 화살 내역 사용자 필드명 수정(#43)

sender, receiver로 주고 받는 사람을 잘 표현할 수 있도록 필드명 수정.
사용 위치 수정

* feat: 특정 날짜 추천 내역 존재 확인 로직 정의(#43)

추천 받은 사용자와 추천 날짜를 기반으로 내역 존재를 확인하는 로직 정의

* feat: 추천할 사용자가 없는 경우 예외 정의(#43)

추천할 사용자가 1명이하인 예외 상황 정의

* refactor: 기존 예외, 화살이 부족한 예외 상황 전부 처리토록 변경(#43)

- 기존 예외는 화살이 부족하여 화살을 보낼 수 없는 예외
- 화살이 없는 상황을 전부 처리할 수 있도록 변경

* feat: 추천 날짜에 추천 내역이 있는 지 확인하는 로직 추가(#43)

* refactor: 사용자 엔티티 수정(#43)

- 생성자의 잘못된 inactive 필드 값 설정, false를 설정토록 수정
- 화살 감소 로직에서 사용되는 예외 변경에 따른 수정

* feat: 이성 추천시 동성을 제외하는 조건 추가(#43)

'이성 추천'이라는 표현에 맞게 조회하는 사용자와 다른 성별의 사용자만
조회되도록 로직에 조건 추가

* feat: 이성 추천 로직에 검증 로직 추가(#43)

- 이성 추천은 하루에 한 번까지만 무료
- 따라서 2번째 추천부터는 화살을 5개씩 소모하도록 로직 추가
- 추천 가능한 사용자가 1명이하인 추천 불가 상황 예외로 처리

* refactor: 병합으로 인한 코드 수정(#43)

dev 브랜치 작업내역 병합으로 인한 코드 수정

* feat: dev 브랜치 병합 과정에서 미반영된 부분 반영(#43)

* feat: 패키지 경로 이동(#43)

* feat: 조회 기준 공백값도 검증하도록 수정(#43)

* feat: 즐겨찾기 여부, 기본 타입으로 변경(#43)

* feat: 첫/마지막 페이지 여부, 기본 타입으로 변경(#43)

* feat: 상속 인터페이스 패키지 변경에 따른 수정(#43)

* feat: 이성 목록 조회 연관 메서드명 수정(#43)

* feat: 이성 목록 조회 연관 메서드명 수정(#43)

* feat: 이성 목록 조회 연관 메서드 수정(#43)

- 메서드명 수정
- 불필요하게 상수로 정의된 값들 지역 변수로 변경

* feat: 메서드명 및 로직 수정(#43)

- 이성 목록 조회 API 메서드명 및 로직 수정
- 다른 사용자 프로필 조회 메서드명 수정

* feat: 메서드명 수정(#43)

* feat: 이성 목록 조회 요청 DTO 수정(#43)

- 즐겨찾는 이성 조회 기준, FAVORITES로 변경
- 페이지 번호 nullable하게 변경

* feat: 추천 이성 조회 메서드, 사용자 성별 파라미터 추가(#43)

* feat: 이성 목록 조회 로직 수정(#43)

- 응답 DTO, 즐겨찾기 존재 여부 필드명 변경에 따른 조회 로직 수정
- 추천 이성 조회 로직, 사용자 성별 파라미터 추가
- 추천일 파라미터명 변경

* feat: 메서드명, 파라미터명 변경(#43)

* feat: ID 기반 사용자 존재 확인 로직 추가(#43)

* feat: 이성 목록 조회 로직 수정(#43)

- 이성 목록 조회시 조회하는 사용자 존재 여부 검증 추가
- 추천 이성 조회시 고정 2 사이즈의 첫 페이지를 조회하도록 수정
- 추천 이성 조회시 사용자 성별 전달하도록 수정

* feat: 추천일 변수명 변경(#43)

* feat: 이성 목록 조회 로직 수정(#43)

- 차단한 사용자 제외 조건 추가
- 같은 날 추천/검색된 사용자 제외 조건 메서드명 수정

* refactor: 코드 정렬 수정(#43)

* docs: 조회 기준 필드 명세 수정(#43)

* feat: 추천 이성 조회 API 추가(#43)

- 기존 이성 목록 조회 API에서 추천 이성 목록을 함께 제공할 경우 로직이 부자연스럽게 구성
- 추천 이성 조회 API를 별도로 분리하여 코드 개선

* refactor: 코드 정렬 수정(#43)

* feat: 이성 목록 조회 조건, 별도 메서드로 분리(#43)

조회 쿼리와 카운팅 쿼리에서 중복하여 사용되는 조회 조건, 별도 메서드 분리하여 코드 중복 제거

* feat: dev 브랜치 병합 과정에서 잘못된 병합된 내역 수정(#43)

* docs: 불필요한 주석 삭제(#43)

* feat: dev 브랜치 병합 과정서 잘못 반영된 내역 삭제(#43)
  • Loading branch information
Minjae-An authored May 14, 2024
1 parent 970adeb commit a123794
Show file tree
Hide file tree
Showing 20 changed files with 746 additions and 197 deletions.
1 change: 0 additions & 1 deletion be/src/main/java/yeonba/be/exception/ArrowException.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
@AllArgsConstructor
public enum ArrowException implements BaseException {


EXCEEDED_DAILY_AD_VIEWS(
HttpStatus.BAD_REQUEST,
"1일 광고 시청은 최대 3회입니다."),
Expand Down
6 changes: 5 additions & 1 deletion be/src/main/java/yeonba/be/exception/UserException.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ public enum UserException implements BaseException {

LOWER_BOUND_LESS_THAN_OR_EQUAL_UPPER_BOUND(
HttpStatus.BAD_REQUEST,
"하한 값은 상한 값보다 작거나 같아야 합니다.");
"하한 값은 상한 값보다 작거나 같아야 합니다."),

NO_MORE_USERS_TO_RECOMMEND(
HttpStatus.BAD_REQUEST,
"더 이상 추천할 사용자가 존재하지 않습니다");

private final HttpStatus httpStatus;
private final String reason;
Expand Down
38 changes: 24 additions & 14 deletions be/src/main/java/yeonba/be/user/controller/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -37,28 +38,38 @@ public class UserController {
private final ReportService reportService;
private final UserService userService;

@Operation(
summary = "이성(다른 사용자) 목록 조회",
description = "조건에 따라 다른 사용자 프로필 목록을 조회할 수 있습니다."
)
@ApiResponse(
responseCode = "200",
description = "이성 목록 정상 조회"
)
@Operation(summary = "이성 목록 조회", description = "이성 목록을 조회할 수 있다.")
@ApiResponse(responseCode = "200", description = "이성 목록 정상 조회")
@GetMapping("/users")
public ResponseEntity<CustomResponse<UserQueryPageResponse>> users(
@ParameterObject UserQueryRequest request) {
public ResponseEntity<CustomResponse<UserQueryPageResponse>> getUsers(
@RequestAttribute("userId") long userId,
@Valid @ParameterObject UserQueryRequest request) {

UserQueryPageResponse response = userService.findUsersByQueryCondition(userId, request);

return ResponseEntity
.ok()
.body(new CustomResponse<>());
.body(new CustomResponse<>(response));
}

@Operation(summary = "추천 이성 조회", description = "추천 이성을 조회할 수 있다.")
@ApiResponse(responseCode = "200", description = "추천 이성 정상 조회")
@GetMapping("/users/recommend")
public ResponseEntity<CustomResponse<UserQueryPageResponse>> getRecommendUsers(
@RequestAttribute("userId") long userId) {

LocalDate recommendDay = LocalDate.now();
UserQueryPageResponse response = userService.findRecommendUsers(userId, recommendDay);

return ResponseEntity
.ok()
.body(new CustomResponse<>(response));
}

@Operation(summary = "다른 사용자 프로필 조회", description = "다른 사용자의 프로필을 조회할 수 있습니다.")
@ApiResponse(responseCode = "200", description = "사용자 프로필 정상 조회")
@GetMapping("/users/{userId}")
public ResponseEntity<CustomResponse<UserProfileResponse>> profile(
public ResponseEntity<CustomResponse<UserProfileResponse>> getTargetUserProfile(
@RequestAttribute("userId") long userId,
@Parameter(description = "조회대상 사용자 ID", example = "1")
@PathVariable("userId") long targetUserId) {
Expand All @@ -71,7 +82,7 @@ public ResponseEntity<CustomResponse<UserProfileResponse>> profile(
}

@Operation(summary = "즐겨찾기 등록", description = "다른 사용자를 자신의 즐겨찾기에 등록할 수 있습니다.")
@ApiResponse(responseCode = "200", description = "즐겨찾기 등록 정상 처리")
@ApiResponse(responseCode = "202", description = "즐겨찾기 등록 정상 처리")
@PostMapping("/favorites/{userId}")
public ResponseEntity<CustomResponse<Void>> registerFavorite(
@RequestAttribute("userId") long userId,
Expand Down Expand Up @@ -100,7 +111,6 @@ public ResponseEntity<CustomResponse<Void>> deleteFavorite(
.body(new CustomResponse<>());
}


@Operation(summary = "사용자 신고", description = "다른 사용자를 신고할 수 있습니다.")
@ApiResponse(responseCode = "200", description = "신고 정상 처리")
@PostMapping("/users/{userId}/report")
Expand Down
104 changes: 24 additions & 80 deletions be/src/main/java/yeonba/be/user/dto/request/UserQueryRequest.java
Original file line number Diff line number Diff line change
@@ -1,93 +1,37 @@
package yeonba.be.user.dto.request;

import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.Explode;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.PositiveOrZero;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class UserQueryRequest {

@Parameter(
name = "type",
description = """
조회 기준
- 추천 이성(선호 조건 바탕) : RECOMMEND
- 즐겨찾는 이성 : BOOKMARKED
- 나에게 관심 있는 이성(나에게 화살을 보낸 이성) : RECEIVED_ARROWS
- 나에게 화살을 보낸 이성 : SENT_ARROWS
- 검색 : SEARCH
""",
example = "RECOMMEND",
in = ParameterIn.QUERY
)
@NotNull
private String type;

@Parameter(
name = "page",
description = "조회할 페이지 번호, 0부터 시작",
example = "0",
in = ParameterIn.QUERY
)
@NotNull
@PositiveOrZero
private Integer page;

@Parameter(
name = "size",
description = "조회할 데이터 수(페이지 사이즈)",
example = "5",
in = ParameterIn.QUERY
)
@Positive
private Integer size;

@Parameter(
name = "area",
description = "활동 지역, 사용자 검색시 사용",
example = "서울",
in = ParameterIn.QUERY
)
private String area;

@Parameter(
name = "vocalRange",
description = "음역대, 사용자 검색시 사용",
example = "저음",
in = ParameterIn.QUERY
)
private String vocalRange;

@Parameter(
name = "age",
description = "나이 범위(하한,상한), 사용자 검색시 사용",
example = "20,25",
in = ParameterIn.QUERY,
explode = Explode.FALSE
)
private List<Integer> ages;

@Parameter(
name = "height",
description = "키 범위(하한,상한), 사용자 검색시 사용",
example = "160,180",
in = ParameterIn.QUERY,
explode = Explode.FALSE
)
private List<Integer> heights;

@Parameter(
name = "includePreferredAnimal",
description = "선호하는 동물상 포함 검색 여부, 사용자 검색시 사용",
example = "true",
in = ParameterIn.QUERY
)
private Boolean includePreferredAnimal;
@Parameter(
name = "type",
description = """
조회 기준
- 즐겨찾는 이성 : FAVORITES
- 나에게 관심 있는 이성(나에게 화살을 보낸 이성) : ARROW_SENDERS
- 나에게 화살을 보낸 이성 : ARROW_RECEIVERS""",
example = "RECOMMEND",
in = ParameterIn.QUERY)
@NotBlank(message = "조회 기준은 반드시 입력되어야 합니다.")
@Pattern(
regexp = "\\b(FAVORITES|ARROW_SENDERS|ARROW_RECEIVERS)\\b",
message = "조회 기준은 FAVORITES, ARROW_SENDERS, ARROW_RECEIVERS만 허용됩니다.")
private String type;

@Parameter(
name = "page",
description = "조회할 페이지 번호, 기본 첫 페이지(0)",
example = "0",
in = ParameterIn.QUERY)
@PositiveOrZero(message = "페이지 번호는 0이상이어야 합니다.")
private Integer page;
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,54 @@
package yeonba.be.user.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.data.domain.Page;

@Getter
@AllArgsConstructor
public class UserQueryPageResponse {

@Schema(
type = "array",
description = "조회된 이성(사용자) 목록"
)
@Size(min = 3)
private List<UserQueryResponse> users;

@Schema(
type = "number",
description = "조회된 전체 페이지 수",
example = "10"
)
private Integer totalPage;

@Schema(
type = "number",
description = "조회된 전체 데이터 수",
example = "1000"
)
private Long totalElements;

@Schema(
type = "boolean",
description = "첫 페이지 여부",
example = "true"
)
private Boolean isFirstPage;

@Schema(
type = "boolean",
description = "마지막 페이지 여부",
example = "false"
)
private Boolean isLastPage;
@Schema(
type = "array",
description = "조회된 이성(사용자) 목록")
private List<UserQueryResponse> users;

@Schema(
type = "number",
description = "조회된 전체 페이지 수",
example = "10")
private int totalPage;

@Schema(
type = "number",
description = "조회된 전체 데이터 수",
example = "1000")
private long totalElements;

@Schema(
type = "boolean",
description = "첫 페이지 여부",
example = "true")
@JsonProperty("isFirst")
private boolean first;

@Schema(
type = "boolean",
description = "마지막 페이지 여부",
example = "false")
@JsonProperty("isLast")
private boolean last;

public static UserQueryPageResponse from(Page<UserQueryResponse> page) {

return new UserQueryPageResponse(
page.getContent(),
page.getTotalPages(),
page.getTotalElements(),
page.isFirst(),
page.isLast());
}
}
Loading

0 comments on commit a123794

Please sign in to comment.