Skip to content

Commit 5b578fe

Browse files
committed
feat: 관리자 - 유저 정보 조회 페이지 개발
1 parent 4007437 commit 5b578fe

File tree

4 files changed

+99
-60
lines changed

4 files changed

+99
-60
lines changed

src/main/kotlin/com/example/jhouse_server/admin/user/dto/AdminUserDto.kt

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.example.jhouse_server.admin.user.dto
22

33
import com.example.jhouse_server.domain.user.entity.Age
4+
import com.example.jhouse_server.domain.user.entity.JoinPath
5+
import com.example.jhouse_server.domain.user.entity.Term
46
import com.example.jhouse_server.domain.user.entity.UserType
57
import com.querydsl.core.annotations.QueryProjection
8+
import org.springframework.format.annotation.DateTimeFormat
69
import java.time.LocalDateTime
710

811
/**
@@ -68,6 +71,33 @@ data class AdminUserList @QueryProjection constructor(
6871
val email: String,
6972
val userType: UserType,
7073
val phoneNum : String,
74+
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
75+
// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
7176
val createdAt : LocalDateTime,
72-
val age: Age
73-
)
77+
val age: String,
78+
val joinPath: Set<String>,
79+
val term : Set<String>,
80+
){
81+
fun getAgeValues() : String{
82+
return Age.valueOf(age).value ?: ""
83+
}
84+
fun getJoinPathValues(): Set<String> {
85+
return joinPath.map { JoinPath.valueOf(it)?.value }.toSet() as Set<String>
86+
}
87+
88+
fun getTermValues(): Set<String> {
89+
return term.map { Term.valueOf(it)?.value }.toSet() as Set<String>
90+
}
91+
92+
}
93+
data class AdminUserQueryResult(
94+
val id: Long,
95+
val nickName: String,
96+
val email: String,
97+
val userType: UserType,
98+
val phoneNum : String,
99+
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
100+
// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
101+
val createdAt : LocalDateTime,
102+
val age: String,
103+
)

src/main/kotlin/com/example/jhouse_server/domain/user/repository/UserRepositoryImpl.kt

+52-10
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import com.example.jhouse_server.admin.anaylsis.dto.AnalysisJoinPathResponse
55
import com.example.jhouse_server.admin.user.dto.AdminAgentSearch
66
import com.example.jhouse_server.admin.user.dto.AdminUserList
77
import com.example.jhouse_server.admin.user.dto.AdminUserWithdrawalSearch
8-
import com.example.jhouse_server.admin.user.dto.QAdminUserList
8+
import com.example.jhouse_server.domain.user.entity.Age
99
import com.example.jhouse_server.domain.user.entity.Authority
1010
import com.example.jhouse_server.domain.user.entity.QUser.user
1111
import com.example.jhouse_server.domain.user.entity.QUserJoinPath.userJoinPath
12+
import com.example.jhouse_server.domain.user.entity.QUserTerm.userTerm
1213
import com.example.jhouse_server.domain.user.entity.User
1314
import com.example.jhouse_server.domain.user.entity.UserType.NONE
1415
import com.example.jhouse_server.domain.user.entity.WithdrawalStatus.WAIT
@@ -19,6 +20,9 @@ import com.example.jhouse_server.domain.user.repository.dto.AdminUserAnalysisAge
1920
import com.example.jhouse_server.domain.user.repository.dto.AdminUserAnalysisJoinPathResult
2021
import com.example.jhouse_server.domain.user.repository.dto.QAdminUserAnalysisAgeResult
2122
import com.example.jhouse_server.domain.user.repository.dto.QAdminUserAnalysisJoinPathResult
23+
import com.querydsl.core.group.GroupBy
24+
import com.querydsl.core.group.GroupBy.groupBy
25+
import com.querydsl.core.types.Projections
2226
import com.querydsl.core.types.dsl.BooleanExpression
2327
import com.querydsl.jpa.impl.JPAQueryFactory
2428
import org.springframework.data.domain.Page
@@ -139,27 +143,65 @@ class UserRepositoryImpl(
139143
adminUserSearch: AdminUserWithdrawalSearch,
140144
pageable: Pageable
141145
): Page<AdminUserList> {
142-
val result = jpaQueryFactory.select(
143-
QAdminUserList(
146+
val result = jpaQueryFactory.selectFrom(user)
147+
.leftJoin(userJoinPath).on(user.id.eq(userJoinPath.user.id))
148+
.leftJoin(userTerm).on(user.id.eq(userTerm.user.id))
149+
.where(searchUserFilter(adminUserSearch), user.authority.eq(Authority.USER))
150+
.offset(pageable.offset)
151+
.limit(pageable.pageSize.toLong())
152+
.transform(groupBy(user.id).list( Projections.constructor(
153+
AdminUserList::class.java,
144154
user.id,
145155
user.nickName,
146156
user.email,
147157
user.userType,
148158
user.phoneNum,
149159
user.createdAt,
150-
user.age
160+
user.age.stringValue(),
161+
GroupBy.set(
162+
userJoinPath.joinPath.stringValue()
163+
),
164+
GroupBy.set(
165+
userTerm.term.stringValue()
166+
)
167+
)))
168+
169+
val map = mutableMapOf<Long, AdminUserList>()
170+
// result의 아이템 중 age, joinPath, term enum의 value로 치환하고 싶어.
171+
result.forEach{
172+
val userId = it.id
173+
val age = Age.valueOf(it.age)
174+
val transformedJoinPath = it.getJoinPathValues().distinct().toSet() ?: it.joinPath
175+
val transformedTerm = it.getTermValues().distinct().toSet() ?: it.term
176+
// map에 userId가 이미 있다면, 해당 userId의 AdminUserList를 가져와서 joinPath, term을 추가하고 다시 map에 넣어줘.
177+
if(map.containsKey(userId)){
178+
val adminUserList = map[userId]
179+
adminUserList?.joinPath?.plusElement(transformedJoinPath)
180+
adminUserList?.term?.plusElement(transformedTerm)
181+
return@forEach
182+
}
183+
map[userId] = AdminUserList(
184+
it.id,
185+
it.nickName,
186+
it.email,
187+
it.userType,
188+
it.phoneNum,
189+
it.createdAt,
190+
age.value,
191+
transformedJoinPath,
192+
transformedTerm
151193
)
152-
).from(user)
153-
.where(searchUserFilter(adminUserSearch))
154-
.offset(pageable.offset)
155-
.limit(pageable.pageSize.toLong())
156-
.fetch()
194+
}
195+
// map의 value만 뽑아서 list로 만들어서 반환해줘.
196+
val transformedResult = map.values.toList()
157197

158198
val countQuery = jpaQueryFactory
159199
.selectFrom(user)
200+
.leftJoin(userJoinPath).on(user.id.eq(userJoinPath.user.id))
201+
.leftJoin(userTerm).on(user.id.eq(userTerm.user.id))
160202
.where(searchUserFilter(adminUserSearch))
161203
.fetch().size.toLong()
162-
return PageImpl(result, pageable, countQuery)
204+
return PageImpl(transformedResult, pageable, countQuery)
163205
}
164206
/**
165207
* ============================================================================================

src/main/resources/templates/house/houseReview.html

+2-19
Original file line numberDiff line numberDiff line change
@@ -58,29 +58,12 @@
5858
<thead>
5959
<tr>
6060
<th scope="col">No.</th>
61-
<th scope="col">
62-
<select class="form-select btn-outline-info" id="scoreFilter" name="scoreFilter" onchange="filterData(this.value, '')">
63-
<option value="">== 거래 만족도 ==</option>
64-
<option value="5" th:selected="${5 == scoreFilter}">매우 만족</option>
65-
<option value="4" th:selected="${4 == scoreFilter}">만족</option>
66-
<option value="3" th:selected="${3 == scoreFilter}">보통</option>
67-
<option value="2" th:selected="${2 == scoreFilter}">불만족</option>
68-
<option value="1" th:selected="${1 == scoreFilter}">매우 불만족</option>
69-
</select>
61+
<th scope="col">거래 만족도
7062
</th>
7163
<th scope="col">거래 날짜(팔린 날짜)</th>
7264
<th scope="col">구매자 전화번호</th>
7365
<th scope="col">구매자 닉네임</th>
74-
<th scope="col">
75-
<select class="form-select btn-outline-info" id="ageFilter" name="ageFilter" onchange="filterData('', this.value)">
76-
<option value="">== 구매자 연령 ==</option>
77-
<option value="TEN" th:selected="${'TEN' == ageFilter}">20대 미만</option>
78-
<option value="TWENTY" th:selected="${'TWENTY' == ageFilter}">20대</option>
79-
<option value="THIRTY" th:selected="${'THIRTY' == ageFilter}">30대</option>
80-
<option value="FORTY" th:selected="${'FORTY' == ageFilter}">40대</option>
81-
<option value="FIFTY" th:selected="${'FIFTY' == ageFilter}">50대</option>
82-
<option value="SIXTY" th:selected="${'SIXTY' == ageFilter}">60대 이상</option>
83-
</select>
66+
<th scope="col">구매자 연령대
8467
</th>
8568
<th scope="col">거래 후기</th>
8669
<th scope="col">빈집 게시글 제목</th>

src/main/resources/templates/user/userManager.html

+13-29
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<style>
1010
.container {
11-
max-width:1000px;
11+
max-width:1600px;
1212
}
1313

1414
.dropdown-item:active {
@@ -54,7 +54,7 @@
5454

5555
<br>
5656

57-
<table class="table" style="vertical-align: middle;">
57+
<table class="table mx-auto" style="vertical-align: middle;">
5858
<thead>
5959
<tr>
6060
<th scope="col">No.</th>
@@ -63,17 +63,9 @@
6363
<th scope="col">유저 타입</th>
6464
<th scope="col">전화번호</th>
6565
<th scope="col">가입일자</th>
66-
<th scope="col">
67-
<select class="form-select btn-outline-info" id="ageFilter" name="ageFilter" onchange="filterData('', this.value)">
68-
<option value="">== 연령 ==</option>
69-
<option value="TEN" th:selected="${'TEN' == ageFilter}">20대 미만</option>
70-
<option value="TWENTY" th:selected="${'TWENTY' == ageFilter}">20대</option>
71-
<option value="THIRTY" th:selected="${'THIRTY' == ageFilter}">30대</option>
72-
<option value="FORTY" th:selected="${'FORTY' == ageFilter}">40대</option>
73-
<option value="FIFTY" th:selected="${'FIFTY' == ageFilter}">50대</option>
74-
<option value="SIXTY" th:selected="${'SIXTY' == ageFilter}">60대 이상</option>
75-
</select>
76-
</th>
66+
<th scope="col">연령대</th>
67+
<th scope="col">가입 경로</th>
68+
<th scope="col">약관 동의 내역</th>
7769
</tr>
7870
</thead>
7971
<tbody>
@@ -99,12 +91,18 @@
9991
<td th:text="${user.age}">
10092
20대
10193
</td>
94+
<td>
95+
<span th:utext="${#strings.setJoin(user.joinPath, '&lt;br /&gt;')}"></span>
96+
</td>
97+
<td>
98+
<span th:utext="${#strings.setJoin(user.term, '&lt;br /&gt;')}"></span>
99+
</td>
102100
</tr>
103101
</tbody>
104102
</table>
105103

106104

107-
<nav aria-label="..." style="position:absolute; left: 50%; transform: translateX(-50%); bottom: 6%;">
105+
<nav aria-label="..." style="position:absolute; left: 50%; ">
108106
<ul class="pagination pagination-sm">
109107
<!-- 이전 버튼-->
110108
<li class="page-item" th:if="${pageCom gt 0}">
@@ -150,21 +148,7 @@
150148
crossorigin="anonymous">
151149
</script>
152150
<script th:inline="javascript">
153-
function filterData(score, age){
154-
const searchFilter = $('#searchFilter').val();
155-
const searchKeyword = $('#searchKeyword').val();
156-
const sendDTO = {
157-
searchFilter : searchFilter,
158-
searchKeyword : searchKeyword,
159-
};
160-
$.ajax({
161-
url: "/admin/user/manage",
162-
data: sendDTO,
163-
type: 'GET',
164-
}).done(function (data){
165-
$("#resultDiv").replaceWith(data);
166-
});
167-
}
151+
168152
</script>
169153
</body>
170154
</html>

0 commit comments

Comments
 (0)