Skip to content

Commit b63e5d4

Browse files
authored
Merge pull request #46 from f-lab-edu/feature/feature#44_search
feature#44 search 검색 후 화면 개발
2 parents da3fbda + 5d32505 commit b63e5d4

31 files changed

+1349
-133
lines changed

core/database/src/main/java/com/chan/database/AppDatabase.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,36 @@ val MIGRATION_13_14 = object : Migration(13, 14) {
4242
}
4343
}
4444

45+
val MIGRATION_14_15 = object : Migration(14, 15) {
46+
override fun migrate(db: SupportSQLiteDatabase) {
47+
db.execSQL("""
48+
CREATE TABLE IF NOT EXISTS `products` (
49+
`productId` TEXT NOT NULL,
50+
`productName` TEXT NOT NULL,
51+
`brandName` TEXT NOT NULL,
52+
`imageUrl` TEXT NOT NULL,
53+
`originalPrice` INTEGER NOT NULL,
54+
`discountPercent` INTEGER NOT NULL,
55+
`discountPrice` INTEGER NOT NULL,
56+
`tags` TEXT NOT NULL,
57+
`reviewRating` REAL NOT NULL,
58+
`reviewCount` INTEGER NOT NULL,
59+
`categoryIds` TEXT NOT NULL,
60+
PRIMARY KEY(`productId`)
61+
)
62+
""".trimIndent())
63+
64+
db.execSQL("""
65+
CREATE TABLE IF NOT EXISTS `category` (
66+
`id` TEXT NOT NULL,
67+
`name` TEXT NOT NULL,
68+
`parentCategoryId` TEXT,
69+
PRIMARY KEY(`id`)
70+
)
71+
""".trimIndent())
72+
}
73+
}
74+
4575
@Database(
4676
entities = [HomeBannerEntity::class, ProductEntity::class, ProductDetailEntity::class, UserEntity::class, SearchHistoryEntity::class, CommonProductEntity::class, CommonCategoryEntity::class],
4777
version = 15,

core/database/src/main/java/com/chan/database/DatabaseModule.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ object DatabaseModule {
3737
fun provideDatabase(
3838
@ApplicationContext context: Context,
3939
productDao: Provider<ProductDao>,
40-
productsDao: Provider<ProductsDao>
40+
productsDao: Provider<ProductsDao>,
41+
categoryDao: Provider<CategoryDao>,
4142
): AppDatabase {
4243
return Room.databaseBuilder(
4344
context,
@@ -49,12 +50,13 @@ object DatabaseModule {
4950
super.onCreate(db)
5051
CoroutineScope(Dispatchers.IO).launch {
5152
insertAllProducts(context, productsDao.get())
52-
products(context, productDao.get())
53+
insertAllCategories(context, categoryDao.get())
54+
// products(context, productDao.get())
5355
}
5456
}
5557
}
5658
)
57-
.addMigrations(MIGRATION_13_14)
59+
.addMigrations(MIGRATION_13_14, MIGRATION_14_15)
5860
.build()
5961
}
6062

@@ -82,6 +84,18 @@ object DatabaseModule {
8284
productsDao.insertAllProducts(products)
8385
}
8486

87+
private suspend fun insertAllCategories(context: Context, categoryDao: CategoryDao) {
88+
val fileName = "category.json"
89+
val jsonString = context.assets
90+
.open(fileName)
91+
.bufferedReader()
92+
.use { it.readText() }
93+
94+
val listType = object : TypeToken<List<CommonCategoryEntity>>() {}.type
95+
val products: List<CommonCategoryEntity> = Gson().fromJson(jsonString, listType)
96+
categoryDao.insertAllCategories(products)
97+
}
98+
8599
@Provides
86100
fun provideHomeBannerDao(db: AppDatabase): HomeBannerDao =
87101
db.homeBannerDao()

core/database/src/main/java/com/chan/database/dao/ProductDao.kt

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import androidx.room.OnConflictStrategy
66
import androidx.room.Query
77
import com.chan.database.dto.CategoryDetailTabsDto
88
import com.chan.database.dto.CategoryTabDto
9+
import com.chan.database.dto.FilterCategoriesDto
910
import com.chan.database.entity.ProductEntity
1011

1112
@Dao
@@ -43,7 +44,7 @@ interface ProductDao {
4344
}
4445

4546
//세일 상품 가져오기
46-
suspend fun getSaleProducts(limit : Int): List<ProductEntity.Categories.SubCategories.Products> {
47+
suspend fun getSaleProducts(limit: Int): List<ProductEntity.Categories.SubCategories.Products> {
4748
return getAll()
4849
.flatMap { it.categories }
4950
.flatMap { it.subCategories }
@@ -52,38 +53,4 @@ interface ProductDao {
5253
.shuffled()
5354
.take(limit)
5455
}
55-
56-
// 동일한 상위 카테고리에 속한 모든 하위 카테고리 정보(id, name) 가져오기
57-
suspend fun getSiblingSubCategories(subCategoryId: String): List<CategoryDetailTabsDto> {
58-
return getAll()
59-
.flatMap { it.categories }
60-
.firstOrNull { category ->
61-
category.subCategories.any { it.categoryId == subCategoryId }
62-
}
63-
?.subCategories
64-
?.map { CategoryDetailTabsDto(categoryId = it.categoryId, categoryName = it.categoryName) }
65-
?: emptyList()
66-
}
67-
68-
//해당 카테고리에 따른 리스트 상품 가져오기
69-
suspend fun getProductsBySubCategoryId(subCategoryId: String): List<ProductEntity.Categories.SubCategories.Products> {
70-
return getAll()
71-
.flatMap { it.categories }
72-
.flatMap { it.subCategories }
73-
.find { it.categoryId == subCategoryId }
74-
?.products
75-
?: emptyList()
76-
}
77-
78-
// 상품 이름으로 검색하기
79-
suspend fun searchProductsByName(query: String): List<ProductEntity.Categories.SubCategories.Products> {
80-
if (query.isBlank()) {
81-
return emptyList()
82-
}
83-
return getAll()
84-
.flatMap { it.categories }
85-
.flatMap { it.subCategories }
86-
.flatMap { it.products }
87-
.filter { it.productName.contains(query, ignoreCase = true) }
88-
}
8956
}

core/database/src/main/java/com/chan/database/dao/ProductsDao.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ interface ProductsDao {
2929

3030
@Insert(onConflict = OnConflictStrategy.REPLACE)
3131
suspend fun insertAllProducts(products: List<CommonProductEntity>)
32+
33+
//검색어가 포함된 상품
34+
@Query("SELECT * FROM products WHERE productName LIKE '%' || :search || '%'")
35+
suspend fun searchProductsByName(search: String) : List<CommonProductEntity>
3236
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.chan.database.dto
2+
3+
data class FilterCategoriesDto(
4+
val categoryId: String,
5+
val name: String,
6+
val subCategories: List<SubCategoryDto>
7+
) {
8+
data class SubCategoryDto(
9+
val subCategoryId: String,
10+
val subCategoryName: String,
11+
)
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.chan.search.data.mappers
2+
3+
import com.chan.database.entity.CommonCategoryEntity
4+
import com.chan.search.domain.model.CategoryVO
5+
6+
fun CommonCategoryEntity.toCategoryVO(): CategoryVO =
7+
CategoryVO(
8+
id = id,
9+
name = name
10+
)
11+
12+
13+

feature/search/src/main/java/com/chan/search/data/mappers/SearchToDomainMapper.kt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package com.chan.search.data.mappers
22

3+
import com.chan.android.model.ProductModel
4+
import com.chan.database.dto.FilterCategoriesDto
5+
import com.chan.database.entity.CommonProductEntity
36
import com.chan.database.entity.ProductEntity
47
import com.chan.database.entity.search.SearchHistoryEntity
58
import com.chan.domain.ProductVO
9+
import com.chan.domain.ProductsVO
10+
import com.chan.search.domain.model.FilterCategoriesVO
611
import com.chan.search.domain.model.SearchHistoryVO
7-
import com.chan.search.ui.model.SearchHistoryModel
12+
import java.text.NumberFormat
13+
import java.util.Locale
814

915
fun ProductEntity.Categories.SubCategories.Products.toDomain(): ProductVO {
1016
return ProductVO(
@@ -33,4 +39,20 @@ fun SearchHistoryVO.toSearchHistoryEntity(): SearchHistoryEntity {
3339
search = search,
3440
timeStamp = timeStamp
3541
)
42+
}
43+
44+
fun CommonProductEntity.toProductsVO(): ProductsVO {
45+
return ProductsVO(
46+
productId = productId,
47+
productName = productName,
48+
brandName = brandName,
49+
imageUrl = imageUrl,
50+
originalPrice = originalPrice,
51+
discountPercent = discountPercent,
52+
discountPrice = discountPrice,
53+
tags = tags,
54+
reviewRating = reviewRating,
55+
reviewCount = reviewCount,
56+
categoryIds = categoryIds,
57+
)
3658
}

feature/search/src/main/java/com/chan/search/data/repository/SearchRepositoryImpl.kt

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
11
package com.chan.search.data.repository
22

3+
import com.chan.database.dao.CategoryDao
34
import com.chan.database.dao.ProductDao
5+
import com.chan.database.dao.ProductsDao
46
import com.chan.database.dao.SearchHistoryDao
57
import com.chan.domain.ProductVO
8+
import com.chan.domain.ProductsVO
9+
import com.chan.search.data.mappers.toCategoryVO
610
import com.chan.search.data.mappers.toDomain
11+
import com.chan.search.data.mappers.toProductsVO
712
import com.chan.search.data.mappers.toSearchHistoryEntity
13+
import com.chan.search.domain.model.FilterCategoryListVO
814
import com.chan.search.domain.model.SearchHistoryVO
915
import com.chan.search.domain.repository.SearchRepository
1016
import kotlinx.coroutines.flow.Flow
1117
import kotlinx.coroutines.flow.map
1218
import javax.inject.Inject
1319

1420
class SearchRepositoryImpl @Inject constructor(
15-
private val productDao: ProductDao,
16-
private val searchHistoryDao: SearchHistoryDao
21+
private val searchHistoryDao: SearchHistoryDao,
22+
private val productsDao: ProductsDao,
23+
private val categoryDao: CategoryDao
1724
) : SearchRepository {
18-
override suspend fun searchProductName(search: String): List<ProductVO> {
19-
return productDao.searchProductsByName(search).map { it.toDomain() }
25+
override suspend fun searchProductName(search: String): List<ProductsVO> {
26+
return productsDao.searchProductsByName(search).map { it.toProductsVO() }
2027
}
2128

29+
override suspend fun getSearchResultProducts(search: String): List<ProductsVO> {
30+
return productsDao.searchProductsByName(search).map { it.toProductsVO() }
31+
}
2232

2333
override fun getRecentSearches(): Flow<List<SearchHistoryVO>> {
2434
return searchHistoryDao.getRecentSearches()
@@ -41,7 +51,32 @@ class SearchRepositoryImpl @Inject constructor(
4151
return searchHistoryDao.clearAll()
4252
}
4353

44-
override suspend fun getSearchResultProducts(search: String): List<ProductVO> {
45-
return productDao.searchProductsByName(search).map { it.toDomain() }
54+
override suspend fun getFilterCategories(): List<FilterCategoryListVO> {
55+
val allCategories = categoryDao.getAllCategories()
56+
57+
val childrenMap = allCategories
58+
.filter { it.parentCategoryId != null }
59+
.groupBy { it.parentCategoryId!! }
60+
61+
return allCategories
62+
.filter { it.parentCategoryId == null }
63+
.map { parentEntity ->
64+
val childrenEntities = childrenMap[parentEntity.id] ?: emptyList()
65+
66+
FilterCategoryListVO(
67+
parent = parentEntity.toCategoryVO(),
68+
children = childrenEntities.map { it.toCategoryVO() }
69+
)
70+
}
71+
}
72+
73+
//DB 쿼리로 필터링 제한
74+
override suspend fun getFilteredProducts(selectedCategoryId: Set<String>): List<ProductsVO> {
75+
val allProducts = productsDao.getAllProducts()
76+
return allProducts
77+
.filter { product ->
78+
product.categoryIds.any { it in selectedCategoryId }
79+
}
80+
.map { it.toProductsVO() }
4681
}
4782
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.chan.search.domain.model
2+
3+
data class FilterCategoriesVO(
4+
val categoryId: String,
5+
val name: String,
6+
val subCategories: List<SubCategoryVO>
7+
)
8+
9+
data class SubCategoryVO(
10+
val subCategoryId: String,
11+
val subCategoryName: String,
12+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.chan.search.domain.model
2+
3+
data class FilterCategoryListVO(
4+
val parent: CategoryVO,
5+
val children: List<CategoryVO>
6+
)
7+
8+
data class CategoryVO(
9+
val id: String,
10+
val name: String
11+
)

0 commit comments

Comments
 (0)