From 4cd1790b2b67fcefdf7ce171e383371e1365afca Mon Sep 17 00:00:00 2001 From: sgo722 Date: Sat, 10 Jan 2026 21:33:24 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9D=B4=EB=A6=84=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=9D=84=20codeName=20=ED=95=84=EB=93=9C=EB=A7=8C=20=EB=8C=80?= =?UTF-8?q?=EC=83=81=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codel/member/infrastructure/MemberJpaRepository.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/codel/member/infrastructure/MemberJpaRepository.kt b/src/main/kotlin/codel/member/infrastructure/MemberJpaRepository.kt index f1cc9de..c3080d6 100644 --- a/src/main/kotlin/codel/member/infrastructure/MemberJpaRepository.kt +++ b/src/main/kotlin/codel/member/infrastructure/MemberJpaRepository.kt @@ -85,14 +85,17 @@ interface MemberJpaRepository : JpaRepository { WHERE (:status IS NULL OR m.memberStatus = :status) AND ( :keyword IS NULL OR :keyword = '' - OR LOWER(m.email) LIKE LOWER(CONCAT('%', :keyword, '%')) OR LOWER(p.codeName) LIKE LOWER(CONCAT('%', :keyword, '%')) ) + AND (:startDate IS NULL OR m.createdAt >= :startDate) + AND (:endDate IS NULL OR m.createdAt < :endDate) """ ) fun findMembersWithFilterAdvanced( @Param("keyword") keyword: String?, @Param("status") status: MemberStatus?, + @Param("startDate") startDate: LocalDateTime?, + @Param("endDate") endDate: LocalDateTime?, pageable: Pageable ): Page From 2ecd3efbcdd26359e46c691a5fbf6d4837a630a2 Mon Sep 17 00:00:00 2001 From: sgo722 Date: Sat, 10 Jan 2026 21:33:29 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=97=90=20=EA=B0=80=EC=9E=85=EC=9D=BC=20?= =?UTF-8?q?=EA=B8=B0=EC=A4=80=20=EB=82=A0=EC=A7=9C=20=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codel/member/business/MemberService.kt | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/codel/member/business/MemberService.kt b/src/main/kotlin/codel/member/business/MemberService.kt index 827124c..4a649da 100644 --- a/src/main/kotlin/codel/member/business/MemberService.kt +++ b/src/main/kotlin/codel/member/business/MemberService.kt @@ -701,11 +701,32 @@ class MemberService( null } + // 날짜 파싱 - yyyy-MM-dd 형식의 문자열을 LocalDateTime으로 변환 + val startDateTime = if (!startDate.isNullOrBlank()) { + try { + LocalDate.parse(startDate).atStartOfDay() // 00:00:00 + } catch (e: Exception) { + null + } + } else { + null + } + + val endDateTime = if (!endDate.isNullOrBlank()) { + try { + LocalDate.parse(endDate).plusDays(1).atStartOfDay() // 다음 날 00:00:00 (해당일 23:59:59까지 포함) + } catch (e: Exception) { + null + } + } else { + null + } + // 정렬 처리를 위한 새로운 Pageable 생성 val sortedPageable = createSortedPageable(pageable, sort, direction) - // 새로운 메서드 사용 - return memberJpaRepository.findMembersWithFilterAdvanced(keyword, statusEnum, sortedPageable) + // 새로운 메서드 사용 (날짜 파라미터 추가) + return memberJpaRepository.findMembersWithFilterAdvanced(keyword, statusEnum, startDateTime, endDateTime, sortedPageable) } /** From 26f90e1d2b48e8d2bfbf06f7ad6be38b39320dc0 Mon Sep 17 00:00:00 2001 From: sgo722 Date: Sat, 10 Jan 2026 21:33:34 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[feat]=20=ED=9A=8C=EC=9B=90=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=83=81=ED=83=9C=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EC=97=90=20=ED=83=88=ED=87=B4=20=EB=B0=8F=20=EC=98=A4=ED=94=88?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=9E=91=EC=84=B1=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/presentation/AdminController.kt | 65 +++++++++++++++++-- src/main/resources/templates/memberList.html | 28 +++++++- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/codel/admin/presentation/AdminController.kt b/src/main/kotlin/codel/admin/presentation/AdminController.kt index 2dd171f..efeb68b 100644 --- a/src/main/kotlin/codel/admin/presentation/AdminController.kt +++ b/src/main/kotlin/codel/admin/presentation/AdminController.kt @@ -320,7 +320,9 @@ class AdminController( "PENDING" to adminService.countMembersByStatus("PENDING"), "DONE" to adminService.countMembersByStatus("DONE"), "REJECT" to adminService.countMembersByStatus("REJECT"), - "PHONE_VERIFIED" to adminService.countMembersByStatus("PHONE_VERIFIED") + "PHONE_VERIFIED" to adminService.countMembersByStatus("PHONE_VERIFIED"), + "WITHDRAWN" to adminService.countMembersByStatus("WITHDRAWN"), + "PERSONALITY_COMPLETED" to adminService.countMembersByStatus("PERSONALITY_COMPLETED") ) model.addAttribute("members", members) @@ -400,11 +402,21 @@ class AdminController( @GetMapping("/v1/admin/questions/{questionId}/edit") fun editQuestionForm( @PathVariable questionId: Long, + @RequestParam(required = false) keyword: String?, + @RequestParam(required = false) category: String?, + @RequestParam(required = false) isActive: String?, + @RequestParam(required = false, defaultValue = "0") page: Int, + @RequestParam(required = false, defaultValue = "20") size: Int, model: Model ): String { val question = adminService.findQuestionById(questionId) model.addAttribute("question", question) model.addAttribute("categories", QuestionCategory.values()) + model.addAttribute("filterKeyword", keyword) + model.addAttribute("filterCategory", category) + model.addAttribute("filterIsActive", isActive) + model.addAttribute("filterPage", page) + model.addAttribute("filterSize", size) return "questionEditForm" } @@ -415,6 +427,11 @@ class AdminController( @RequestParam category: String, @RequestParam(required = false) description: String?, @RequestParam(defaultValue = "false") isActive: Boolean, + @RequestParam(required = false) keyword: String?, + @RequestParam(required = false) filterCategory: String?, + @RequestParam(required = false) filterIsActive: String?, + @RequestParam(required = false, defaultValue = "0") page: Int, + @RequestParam(required = false, defaultValue = "20") size: Int, redirectAttributes: RedirectAttributes ): String { try { @@ -424,12 +441,27 @@ class AdminController( } catch (e: Exception) { redirectAttributes.addFlashAttribute("error", "질문 수정에 실패했습니다: ${e.message}") } - return "redirect:/v1/admin/questions" + + // 필터 조건 유지하여 리다이렉트 + val params = mutableListOf() + keyword?.let { if (it.isNotBlank()) params.add("keyword=$it") } + filterCategory?.let { if (it.isNotBlank()) params.add("category=$it") } + filterIsActive?.let { if (it.isNotBlank()) params.add("isActive=$it") } + params.add("page=$page") + params.add("size=$size") + + val queryString = if (params.isNotEmpty()) "?${params.joinToString("&")}" else "" + return "redirect:/v1/admin/questions$queryString" } @PostMapping("/v1/admin/questions/{questionId}/delete") fun deleteQuestion( @PathVariable questionId: Long, + @RequestParam(required = false) keyword: String?, + @RequestParam(required = false) category: String?, + @RequestParam(required = false) isActive: String?, + @RequestParam(required = false, defaultValue = "0") page: Int, + @RequestParam(required = false, defaultValue = "20") size: Int, redirectAttributes: RedirectAttributes ): String { try { @@ -438,12 +470,27 @@ class AdminController( } catch (e: Exception) { redirectAttributes.addFlashAttribute("error", "질문 삭제에 실패했습니다: ${e.message}") } - return "redirect:/v1/admin/questions" + + // 필터 조건 유지하여 리다이렉트 + val params = mutableListOf() + keyword?.let { if (it.isNotBlank()) params.add("keyword=$it") } + category?.let { if (it.isNotBlank()) params.add("category=$it") } + isActive?.let { if (it.isNotBlank()) params.add("isActive=$it") } + params.add("page=$page") + params.add("size=$size") + + val queryString = if (params.isNotEmpty()) "?${params.joinToString("&")}" else "" + return "redirect:/v1/admin/questions$queryString" } @PostMapping("/v1/admin/questions/{questionId}/toggle") fun toggleQuestionStatus( @PathVariable questionId: Long, + @RequestParam(required = false) keyword: String?, + @RequestParam(required = false) category: String?, + @RequestParam(required = false) isActive: String?, + @RequestParam(required = false, defaultValue = "0") page: Int, + @RequestParam(required = false, defaultValue = "20") size: Int, redirectAttributes: RedirectAttributes ): String { try { @@ -453,7 +500,17 @@ class AdminController( } catch (e: Exception) { redirectAttributes.addFlashAttribute("error", "질문 상태 변경에 실패했습니다: ${e.message}") } - return "redirect:/v1/admin/questions" + + // 필터 조건 유지하여 리다이렉트 + val params = mutableListOf() + keyword?.let { if (it.isNotBlank()) params.add("keyword=$it") } + category?.let { if (it.isNotBlank()) params.add("category=$it") } + isActive?.let { if (it.isNotBlank()) params.add("isActive=$it") } + params.add("page=$page") + params.add("size=$size") + + val queryString = if (params.isNotEmpty()) "?${params.joinToString("&")}" else "" + return "redirect:/v1/admin/questions$queryString" } // ========== 신고 관리 ========== diff --git a/src/main/resources/templates/memberList.html b/src/main/resources/templates/memberList.html index f663b90..ae3aa8d 100644 --- a/src/main/resources/templates/memberList.html +++ b/src/main/resources/templates/memberList.html @@ -186,8 +186,8 @@

0

거부됨
-

0

- 핸드폰 인증 완료 +

0

+ 탈퇴

0%

@@ -213,6 +213,8 @@

승인 완료 + +

@@ -295,6 +297,22 @@

0 + + @@ -401,6 +419,12 @@

핸드폰 인증 완료 + + 오픈프로필 작성 완료 + + + 탈퇴 + From 1ff03b37a674ed32b493b1f08e7df03fbcaea84c Mon Sep 17 00:00:00 2001 From: sgo722 Date: Sat, 10 Jan 2026 21:33:40 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[feat]=20=EC=A7=88=EB=AC=B8=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95/=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD/?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=ED=9B=84=20=ED=95=84=ED=84=B0=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/templates/questionEditForm.html | 15 ++++++++++++--- src/main/resources/templates/questionList.html | 12 +++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/resources/templates/questionEditForm.html b/src/main/resources/templates/questionEditForm.html index e6d76b3..11afb93 100644 --- a/src/main/resources/templates/questionEditForm.html +++ b/src/main/resources/templates/questionEditForm.html @@ -41,7 +41,8 @@

질문 수정

- + 목록으로
@@ -49,9 +50,16 @@

질문 수정

+ + + + + + +
-
최대 500자까지 입력 가능합니다.
@@ -91,7 +99,8 @@

질문 수정

- + 취소
diff --git a/src/main/resources/templates/questionList.html b/src/main/resources/templates/questionList.html index 311ca34..f7205d2 100644 --- a/src/main/resources/templates/questionList.html +++ b/src/main/resources/templates/questionList.html @@ -119,12 +119,17 @@

질문 관리

- + + + + +

+ + + + +