Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a1b57f90fd | ||
|
e086b3a04b | ||
|
ed634ebd21 | ||
|
c74b39e692 | ||
|
ebade00c96 | ||
|
9a9b066120 | ||
|
710b20c3af | ||
|
105378ca52 |
@ -3,20 +3,7 @@
|
|||||||
<component name="deploymentTargetDropDown">
|
<component name="deploymentTargetDropDown">
|
||||||
<value>
|
<value>
|
||||||
<entry key="app">
|
<entry key="app">
|
||||||
<State>
|
<State />
|
||||||
<targetSelectedWithDropDown>
|
|
||||||
<Target>
|
|
||||||
<type value="QUICK_BOOT_TARGET" />
|
|
||||||
<deviceKey>
|
|
||||||
<Key>
|
|
||||||
<type value="VIRTUAL_DEVICE_PATH" />
|
|
||||||
<value value="$USER_HOME$/.android/avd/Pixel_7_API_33.avd" />
|
|
||||||
</Key>
|
|
||||||
</deviceKey>
|
|
||||||
</Target>
|
|
||||||
</targetSelectedWithDropDown>
|
|
||||||
<timeTargetWasSelectedWithDropDown value="2023-12-20T08:37:35.519292927Z" />
|
|
||||||
</State>
|
|
||||||
</entry>
|
</entry>
|
||||||
</value>
|
</value>
|
||||||
</component>
|
</component>
|
||||||
|
@ -11,7 +11,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.zyzf.coffeepreorder"
|
applicationId = "com.zyzf.coffeepreorder"
|
||||||
minSdk = 24
|
minSdk = 26
|
||||||
targetSdk = 33
|
targetSdk = 33
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
@ -2,7 +2,12 @@ package com.zyzf.coffeepreorder.api
|
|||||||
|
|
||||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||||
import com.zyzf.coffeepreorder.api.model.CoffeeRemote
|
import com.zyzf.coffeepreorder.api.model.CoffeeRemote
|
||||||
|
import com.zyzf.coffeepreorder.api.model.OrderCoffeeCrossRefRemote
|
||||||
|
import com.zyzf.coffeepreorder.api.model.OrderCoffeeRemote
|
||||||
|
import com.zyzf.coffeepreorder.api.model.OrderRemote
|
||||||
import com.zyzf.coffeepreorder.api.model.UserRemote
|
import com.zyzf.coffeepreorder.api.model.UserRemote
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -77,8 +82,44 @@ interface MyServerService {
|
|||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
): CoffeeRemote
|
): CoffeeRemote
|
||||||
|
|
||||||
|
@GET("order/byDate")
|
||||||
|
suspend fun getOrdersByDate(
|
||||||
|
@Query("startDate") startDate: String,
|
||||||
|
@Query("endDate") endDate: String,
|
||||||
|
): List<OrderCoffeeRemote>
|
||||||
|
|
||||||
|
@POST("order/")
|
||||||
|
suspend fun createOrder(
|
||||||
|
@Body order: OrderRemote,
|
||||||
|
): OrderRemote
|
||||||
|
|
||||||
|
@GET("order/byUser")
|
||||||
|
suspend fun getOrdersByUser(
|
||||||
|
@Query("userId") userId: Int,
|
||||||
|
@Query("pageNo") page: Int,
|
||||||
|
@Query("pageSize") limit: Int,
|
||||||
|
): List<OrderRemote>
|
||||||
|
|
||||||
|
@GET("order/coffeesByOrder")
|
||||||
|
suspend fun getCoffeesByOrder(
|
||||||
|
@Query("orderId") orderId: Int,
|
||||||
|
): List<CoffeeRemote>
|
||||||
|
|
||||||
|
@GET("order/coffeeCrossRef")
|
||||||
|
suspend fun getOrderCoffees(): List<OrderCoffeeCrossRefRemote>
|
||||||
|
|
||||||
|
@POST("order/coffeeCrossRef")
|
||||||
|
suspend fun createOrderCoffee(
|
||||||
|
@Body orderCoffee: OrderCoffeeCrossRefRemote,
|
||||||
|
)
|
||||||
|
|
||||||
|
@DELETE("order/coffeeCrossRef/{id}")
|
||||||
|
suspend fun deleteOrderCoffee(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val BASE_URL = "http://192.168.0.100:8080/api/"
|
private const val BASE_URL = "https://api.zyzf.space/api/"
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var INSTANCE: MyServerService? = null
|
private var INSTANCE: MyServerService? = null
|
||||||
|
@ -63,7 +63,7 @@ class CoffeeRemoteMediator(
|
|||||||
val nextKey = if (endOfPaginationReached) null else page + 1
|
val nextKey = if (endOfPaginationReached) null else page + 1
|
||||||
val keys = coffees.map {
|
val keys = coffees.map {
|
||||||
RemoteKeys(
|
RemoteKeys(
|
||||||
entityId = it.uid,
|
entityId = it.coffeeId,
|
||||||
type = RemoteKeyType.COFFEE,
|
type = RemoteKeyType.COFFEE,
|
||||||
prevKey = prevKey,
|
prevKey = prevKey,
|
||||||
nextKey = nextKey
|
nextKey = nextKey
|
||||||
@ -83,14 +83,14 @@ class CoffeeRemoteMediator(
|
|||||||
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Coffee>): RemoteKeys? {
|
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Coffee>): RemoteKeys? {
|
||||||
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
||||||
?.let { coffee ->
|
?.let { coffee ->
|
||||||
dbRemoteKeyRepository.getAllRemoteKeys(coffee.uid, RemoteKeyType.COFFEE)
|
dbRemoteKeyRepository.getAllRemoteKeys(coffee.coffeeId, RemoteKeyType.COFFEE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Coffee>): RemoteKeys? {
|
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Coffee>): RemoteKeys? {
|
||||||
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
||||||
?.let { coffee ->
|
?.let { coffee ->
|
||||||
dbRemoteKeyRepository.getAllRemoteKeys(coffee.uid, RemoteKeyType.COFFEE)
|
dbRemoteKeyRepository.getAllRemoteKeys(coffee.coffeeId, RemoteKeyType.COFFEE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class CoffeeRemoteMediator(
|
|||||||
state: PagingState<Int, Coffee>
|
state: PagingState<Int, Coffee>
|
||||||
): RemoteKeys? {
|
): RemoteKeys? {
|
||||||
return state.anchorPosition?.let { position ->
|
return state.anchorPosition?.let { position ->
|
||||||
state.closestItemToPosition(position)?.uid?.let { coffeeUid ->
|
state.closestItemToPosition(position)?.coffeeId?.let { coffeeUid ->
|
||||||
dbRemoteKeyRepository.getAllRemoteKeys(coffeeUid, RemoteKeyType.COFFEE)
|
dbRemoteKeyRepository.getAllRemoteKeys(coffeeUid, RemoteKeyType.COFFEE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,14 +47,14 @@ class RestCoffeeRepository(
|
|||||||
service.getCoffee(uid).toCoffee()
|
service.getCoffee(uid).toCoffee()
|
||||||
|
|
||||||
override suspend fun insert(coffee: Coffee): Long {
|
override suspend fun insert(coffee: Coffee): Long {
|
||||||
return service.createCoffee(coffee.toCoffeeRemote()).toCoffee().uid.toLong()
|
return service.createCoffee(coffee.toCoffeeRemote()).toCoffee().coffeeId.toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun update(coffee: Coffee): Int {
|
override suspend fun update(coffee: Coffee): Int {
|
||||||
return service.updateCoffee(coffee.uid, coffee.toCoffeeRemote()).toCoffee().uid
|
return service.updateCoffee(coffee.coffeeId, coffee.toCoffeeRemote()).toCoffee().coffeeId
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(coffee: Coffee) {
|
override suspend fun delete(coffee: Coffee) {
|
||||||
service.deleteCoffee(coffee.uid).toCoffee()
|
service.deleteCoffee(coffee.coffeeId).toCoffee()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,7 @@ fun CoffeeRemote.toCoffee(): Coffee = Coffee(
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun Coffee.toCoffeeRemote(): CoffeeRemote = CoffeeRemote(
|
fun Coffee.toCoffeeRemote(): CoffeeRemote = CoffeeRemote(
|
||||||
uid,
|
coffeeId,
|
||||||
name,
|
name,
|
||||||
cost,
|
cost,
|
||||||
ingredients
|
ingredients
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.model
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OrderCoffeeCrossRefRemote(
|
||||||
|
val orderId: Int = 0,
|
||||||
|
val coffeeId: Int = 0,
|
||||||
|
val count: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderCoffeeCrossRefRemote.toOrderCoffeeCrossRef(): OrderCoffeeCrossRef = OrderCoffeeCrossRef(
|
||||||
|
orderId,
|
||||||
|
coffeeId,
|
||||||
|
count
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderCoffeeCrossRef.toOrderCoffeeCrossRefRemote(): OrderCoffeeCrossRefRemote = OrderCoffeeCrossRefRemote(
|
||||||
|
orderId,
|
||||||
|
coffeeId,
|
||||||
|
count
|
||||||
|
)
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.model
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OrderCoffeeRemote(
|
||||||
|
val order: OrderRemote = OrderRemote(),
|
||||||
|
val coffees: List<Pair<CoffeeRemote, Int>> = listOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderCoffeeRemote.toOrderWithCoffees(): OrderWithCoffees {
|
||||||
|
val convertedOrder = this.order.toOrder()
|
||||||
|
val convertedCoffees: List<Coffee> = this.coffees.map { it.first.toCoffee() }
|
||||||
|
val convertedCounts: List<Int> = this.coffees.map { it.second }
|
||||||
|
return OrderWithCoffees(convertedOrder, order.user.toUser(), convertedCoffees, convertedCounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OrderWithCoffees.toOrderCoffeeRemote(): OrderCoffeeRemote {
|
||||||
|
val convertedOrder = this.order.toOrderRemote()
|
||||||
|
var convertedCoffees = ArrayList<Pair<CoffeeRemote, Int>>()
|
||||||
|
for (i in (0..this.coffees.count()-1)) {
|
||||||
|
convertedCoffees.add(Pair(this.coffees[i].toCoffeeRemote(), this.counts[i]))
|
||||||
|
}
|
||||||
|
return OrderCoffeeRemote(convertedOrder, convertedCoffees)
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.model
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.user.RestUserRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDataContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OrderRemote(
|
||||||
|
val id: Int = 0,
|
||||||
|
val date: String = "",
|
||||||
|
val user: UserRemote = User.getUser().toUserRemote(),
|
||||||
|
val sum: Double = 0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderRemote.toOrder(): Order = Order(
|
||||||
|
id,
|
||||||
|
date,
|
||||||
|
user.id,
|
||||||
|
sum
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Order.toOrderRemote(): OrderRemote = OrderRemote(
|
||||||
|
orderId,
|
||||||
|
date,
|
||||||
|
temp(userId),
|
||||||
|
sum
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun temp(userId: Int): UserRemote {
|
||||||
|
var user: UserRemote? = null
|
||||||
|
runBlocking {
|
||||||
|
user = MyServerService.getInstance().getUser(userId)
|
||||||
|
}
|
||||||
|
return user!!
|
||||||
|
}
|
@ -23,7 +23,7 @@ fun UserRemote.toUser(): User = User(
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun User.toUserRemote(): UserRemote = UserRemote(
|
fun User.toUserRemote(): UserRemote = UserRemote(
|
||||||
uid,
|
userId,
|
||||||
login,
|
login,
|
||||||
fio,
|
fio,
|
||||||
phone,
|
phone,
|
||||||
|
@ -0,0 +1,111 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.order
|
||||||
|
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.LoadType
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import androidx.paging.RemoteMediator
|
||||||
|
import androidx.room.withTransaction
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toCoffee
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toOrder
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeyType
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineOrderRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
class OrderRemoteMediator(
|
||||||
|
private val service: MyServerService,
|
||||||
|
private val dbOrderRepository: OfflineOrderRepository,
|
||||||
|
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||||
|
private val database: AppDatabase
|
||||||
|
) : RemoteMediator<Int, Order>() {
|
||||||
|
|
||||||
|
override suspend fun initialize(): InitializeAction {
|
||||||
|
return InitializeAction.LAUNCH_INITIAL_REFRESH
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(
|
||||||
|
loadType: LoadType,
|
||||||
|
state: PagingState<Int, Order>
|
||||||
|
): MediatorResult {
|
||||||
|
val page = when (loadType) {
|
||||||
|
LoadType.REFRESH -> {
|
||||||
|
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
|
||||||
|
remoteKeys?.nextKey?.minus(1) ?: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadType.PREPEND -> {
|
||||||
|
val remoteKeys = getRemoteKeyForFirstItem(state)
|
||||||
|
remoteKeys?.prevKey
|
||||||
|
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadType.APPEND -> {
|
||||||
|
val remoteKeys = getRemoteKeyForLastItem(state)
|
||||||
|
remoteKeys?.nextKey
|
||||||
|
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val orders = service.getOrdersByUser(CoffeeApplication.currentUser!!.userId, page, state.config.pageSize).map { it.toOrder() }
|
||||||
|
val endOfPaginationReached = orders.isEmpty()
|
||||||
|
database.withTransaction {
|
||||||
|
if (loadType == LoadType.REFRESH) {
|
||||||
|
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.ORDER)
|
||||||
|
dbOrderRepository.deleteAll()
|
||||||
|
}
|
||||||
|
val prevKey = if (page == 1) null else page - 1
|
||||||
|
val nextKey = if (endOfPaginationReached) null else page + 1
|
||||||
|
val keys = orders.map {
|
||||||
|
RemoteKeys(
|
||||||
|
entityId = it.orderId,
|
||||||
|
type = RemoteKeyType.ORDER,
|
||||||
|
prevKey = prevKey,
|
||||||
|
nextKey = nextKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dbRemoteKeyRepository.createRemoteKeys(keys)
|
||||||
|
dbOrderRepository.insertOrders(orders)
|
||||||
|
}
|
||||||
|
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
return MediatorResult.Error(exception)
|
||||||
|
} catch (exception: HttpException) {
|
||||||
|
return MediatorResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Order>): RemoteKeys? {
|
||||||
|
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
||||||
|
?.let { order ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(order.orderId, RemoteKeyType.ORDER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Order>): RemoteKeys? {
|
||||||
|
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
||||||
|
?.let { order ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(order.orderId, RemoteKeyType.ORDER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyClosestToCurrentPosition(
|
||||||
|
state: PagingState<Int, Order>
|
||||||
|
): RemoteKeys? {
|
||||||
|
return state.anchorPosition?.let { position ->
|
||||||
|
state.closestItemToPosition(position)?.orderId?.let { orderUid ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(orderUid, RemoteKeyType.ORDER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.order
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toOrderCoffeeCrossRef
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toOrderCoffeeCrossRefRemote
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineOrderWithCoffeesRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OrderWithCoffeesRepository
|
||||||
|
|
||||||
|
class RestOrderCoffeesRepository (
|
||||||
|
private val service: MyServerService,
|
||||||
|
private val dbOrderCoffeeRepository: OfflineOrderWithCoffeesRepository,
|
||||||
|
): OrderWithCoffeesRepository {
|
||||||
|
override suspend fun getAll(): List<OrderCoffeeCrossRef> {
|
||||||
|
Log.d(RestOrderCoffeesRepository::class.simpleName, "Get OrderCoffees")
|
||||||
|
|
||||||
|
val existOrderCoffees = dbOrderCoffeeRepository.getAll().toMutableList()
|
||||||
|
val serverOrderCoffees = service.getOrderCoffees().map { it.toOrderCoffeeCrossRef() }
|
||||||
|
|
||||||
|
// Найти записи для удаления (те, что есть в БД, но отсутствуют на сервере)
|
||||||
|
val toDelete = existOrderCoffees.filterNot { serverOrderCoffees.contains(it) }
|
||||||
|
toDelete.forEach { dbOrderCoffeeRepository.delete(it) }
|
||||||
|
|
||||||
|
// Найти новые записи для добавления (те, что есть на сервере, но отсутствуют в БД)
|
||||||
|
val toAdd = serverOrderCoffees.filterNot { existOrderCoffees.contains(it) }
|
||||||
|
toAdd.forEach { dbOrderCoffeeRepository.insert(it) }
|
||||||
|
|
||||||
|
// Вернуть обновленный список записей из БД
|
||||||
|
return dbOrderCoffeeRepository.getAll()
|
||||||
|
}
|
||||||
|
override suspend fun insert(orderCoffee: OrderCoffeeCrossRef) {
|
||||||
|
service.createOrderCoffee(orderCoffee.toOrderCoffeeCrossRefRemote())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(orderCoffee: OrderCoffeeCrossRef) {
|
||||||
|
service.deleteOrderCoffee(orderCoffee.orderId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.order
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toCoffee
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toOrder
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toOrderRemote
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toOrderWithCoffees
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineOrderRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OrderRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
|
class RestOrderRepository(
|
||||||
|
private val service: MyServerService,
|
||||||
|
private val dbOrderRepository: OfflineOrderRepository,
|
||||||
|
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||||
|
private val database: AppDatabase
|
||||||
|
) : OrderRepository {
|
||||||
|
override suspend fun getCoffeesByOrder(orderId: Int): Flow<List<Coffee>> {
|
||||||
|
return flowOf(service.getCoffeesByOrder(orderId).map { it.toCoffee() })
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getOrdersByDate(startDate: String, endDate: String): Flow<List<OrderWithCoffees>> {
|
||||||
|
return flowOf(service.getOrdersByDate(startDate, endDate).map { it.toOrderWithCoffees() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
override fun getOrdersByUser(id: Int): Flow<PagingData<Order>> {
|
||||||
|
Log.d(RestOrderRepository::class.simpleName, "Get Orders by User")
|
||||||
|
|
||||||
|
val pagingSourceFactory = { dbOrderRepository.getOrdersByUserPagingSource(id) }
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
remoteMediator = OrderRemoteMediator(
|
||||||
|
service,
|
||||||
|
dbOrderRepository,
|
||||||
|
dbRemoteKeyRepository,
|
||||||
|
database
|
||||||
|
),
|
||||||
|
pagingSourceFactory = pagingSourceFactory
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insert(order: Order): Long {
|
||||||
|
return service.createOrder(order.toOrderRemote()).toOrder().orderId.toLong()
|
||||||
|
}
|
||||||
|
}
|
@ -44,21 +44,21 @@ class RestUserRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getByUid(uid: Int): User =
|
override suspend fun getByUid(uid: Int): User =
|
||||||
service.getUser(uid).toUser()!!
|
service.getUser(uid).toUser()
|
||||||
|
|
||||||
override suspend fun tryLogin(login: String, password: String): User? =
|
override suspend fun tryLogin(login: String, password: String): User =
|
||||||
service.tryLogin(login, password).toUser()
|
service.tryLogin(login, password).toUser()
|
||||||
|
|
||||||
|
|
||||||
override suspend fun insert(user: User): Long {
|
override suspend fun insert(user: User): Long {
|
||||||
return service.createUser(user.toUserRemote()).toUser()?.uid?.toLong()!!
|
return service.createUser(user.toUserRemote()).toUser().userId.toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun update(user: User): Int {
|
override suspend fun update(user: User): Int {
|
||||||
return service.updateUser(user.uid, user.toUserRemote()).toUser()?.uid!!
|
return service.updateUser(user.userId, user.toUserRemote()).toUser().userId
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(user: User) {
|
override suspend fun delete(user: User) {
|
||||||
service.deleteUser(user.uid).toUser()
|
service.deleteUser(user.userId).toUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -63,7 +63,7 @@ class UserRemoteMediator(
|
|||||||
val nextKey = if (endOfPaginationReached) null else page + 1
|
val nextKey = if (endOfPaginationReached) null else page + 1
|
||||||
val keys = users.map {
|
val keys = users.map {
|
||||||
RemoteKeys(
|
RemoteKeys(
|
||||||
entityId = it.uid,
|
entityId = it.userId,
|
||||||
type = RemoteKeyType.USER,
|
type = RemoteKeyType.USER,
|
||||||
prevKey = prevKey,
|
prevKey = prevKey,
|
||||||
nextKey = nextKey
|
nextKey = nextKey
|
||||||
@ -83,14 +83,14 @@ class UserRemoteMediator(
|
|||||||
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, User>): RemoteKeys? {
|
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, User>): RemoteKeys? {
|
||||||
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
||||||
?.let { user ->
|
?.let { user ->
|
||||||
dbRemoteKeyRepository.getAllRemoteKeys(user.uid, RemoteKeyType.USER)
|
dbRemoteKeyRepository.getAllRemoteKeys(user.userId, RemoteKeyType.USER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, User>): RemoteKeys? {
|
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, User>): RemoteKeys? {
|
||||||
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
||||||
?.let { user ->
|
?.let { user ->
|
||||||
dbRemoteKeyRepository.getAllRemoteKeys(user.uid, RemoteKeyType.USER)
|
dbRemoteKeyRepository.getAllRemoteKeys(user.userId, RemoteKeyType.USER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class UserRemoteMediator(
|
|||||||
state: PagingState<Int, User>
|
state: PagingState<Int, User>
|
||||||
): RemoteKeys? {
|
): RemoteKeys? {
|
||||||
return state.anchorPosition?.let { position ->
|
return state.anchorPosition?.let { position ->
|
||||||
state.closestItemToPosition(position)?.uid?.let { userUid ->
|
state.closestItemToPosition(position)?.userId?.let { userUid ->
|
||||||
dbRemoteKeyRepository.getAllRemoteKeys(userUid, RemoteKeyType.USER)
|
dbRemoteKeyRepository.getAllRemoteKeys(userUid, RemoteKeyType.USER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,14 @@ package com.zyzf.coffeepreorder.database
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.zyzf.coffeepreorder.api.MyServerService
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
import com.zyzf.coffeepreorder.api.coffee.RestCoffeeRepository
|
import com.zyzf.coffeepreorder.api.coffee.RestCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.api.order.RestOrderCoffeesRepository
|
||||||
|
import com.zyzf.coffeepreorder.api.order.RestOrderRepository
|
||||||
import com.zyzf.coffeepreorder.api.user.RestUserRepository
|
import com.zyzf.coffeepreorder.api.user.RestUserRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.OfflineCartRepository
|
import com.zyzf.coffeepreorder.database.repository.OfflineCartRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
|
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineOrderRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineOrderWithCoffeesRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
|
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
|
||||||
|
|
||||||
@ -14,6 +18,8 @@ interface AppContainer {
|
|||||||
val cartRepository: CartRepository
|
val cartRepository: CartRepository
|
||||||
val coffeeRestRepository: RestCoffeeRepository
|
val coffeeRestRepository: RestCoffeeRepository
|
||||||
val userRestRepository: RestUserRepository
|
val userRestRepository: RestUserRepository
|
||||||
|
val orderRestRepository: RestOrderRepository
|
||||||
|
val orderCoffeesRestRepository: RestOrderCoffeesRepository
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val LIMIT = 10
|
const val LIMIT = 10
|
||||||
@ -27,6 +33,12 @@ class AppDataContainer(private val context: Context) : AppContainer {
|
|||||||
private val userRepository: OfflineUserRepository by lazy {
|
private val userRepository: OfflineUserRepository by lazy {
|
||||||
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
||||||
}
|
}
|
||||||
|
private val orderRepository: OfflineOrderRepository by lazy {
|
||||||
|
OfflineOrderRepository(AppDatabase.getInstance(context).orderDao())
|
||||||
|
}
|
||||||
|
private val orderCoffeesRepository: OfflineOrderWithCoffeesRepository by lazy {
|
||||||
|
OfflineOrderWithCoffeesRepository(AppDatabase.getInstance(context).orderWithCoffeesDao())
|
||||||
|
}
|
||||||
override val cartRepository: CartRepository by lazy {
|
override val cartRepository: CartRepository by lazy {
|
||||||
OfflineCartRepository(AppDatabase.getInstance(context).cartDao())
|
OfflineCartRepository(AppDatabase.getInstance(context).cartDao())
|
||||||
}
|
}
|
||||||
@ -49,4 +61,18 @@ class AppDataContainer(private val context: Context) : AppContainer {
|
|||||||
AppDatabase.getInstance(context)
|
AppDatabase.getInstance(context)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
override val orderRestRepository: RestOrderRepository by lazy {
|
||||||
|
RestOrderRepository(
|
||||||
|
MyServerService.getInstance(),
|
||||||
|
orderRepository,
|
||||||
|
remoteKeyRepository,
|
||||||
|
AppDatabase.getInstance(context)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override val orderCoffeesRestRepository: RestOrderCoffeesRepository by lazy {
|
||||||
|
RestOrderCoffeesRepository(
|
||||||
|
MyServerService.getInstance(),
|
||||||
|
orderCoffeesRepository
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,18 +6,24 @@ import androidx.room.Room
|
|||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import com.zyzf.coffeepreorder.database.dao.CartDao
|
import com.zyzf.coffeepreorder.database.dao.CartDao
|
||||||
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
|
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.OrderDao
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.OrderWithCoffees
|
||||||
import com.zyzf.coffeepreorder.database.dao.RemoteKeysDao
|
import com.zyzf.coffeepreorder.database.dao.RemoteKeysDao
|
||||||
import com.zyzf.coffeepreorder.database.dao.UserDao
|
import com.zyzf.coffeepreorder.database.dao.UserDao
|
||||||
import com.zyzf.coffeepreorder.database.model.Cart
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
|
||||||
@Database(entities = [User::class, Coffee::class, Cart::class, RemoteKeys::class], version = 1, exportSchema = false)
|
@Database(entities = [User::class, Coffee::class, Cart::class, Order::class, OrderCoffeeCrossRef::class, RemoteKeys::class], version = 1, exportSchema = false)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun userDao(): UserDao
|
abstract fun userDao(): UserDao
|
||||||
abstract fun coffeeDao(): CoffeeDao
|
abstract fun coffeeDao(): CoffeeDao
|
||||||
abstract fun cartDao(): CartDao
|
abstract fun cartDao(): CartDao
|
||||||
|
abstract fun orderDao(): OrderDao
|
||||||
|
abstract fun orderWithCoffeesDao(): OrderWithCoffees
|
||||||
abstract fun remoteKeysDao(): RemoteKeysDao
|
abstract fun remoteKeysDao(): RemoteKeysDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -14,14 +14,17 @@ interface CartDao {
|
|||||||
@Query("select * from cart")
|
@Query("select * from cart")
|
||||||
suspend fun getAll(): Cart
|
suspend fun getAll(): Cart
|
||||||
|
|
||||||
@Query("select coffee.uid, coffee.name, coffee.cost, coffee.ingredients from cart join coffee on coffee.uid = cart.coffee_id and cart.count > 0 collate nocase")
|
@Query("select coffee.* from cart join coffee on coffee.coffee_id = cart.coffee_id and cart.count > 0 collate nocase")
|
||||||
fun getAllInCart(): PagingSource<Int, Coffee>
|
fun getAllInCart(): PagingSource<Int, Coffee>
|
||||||
|
|
||||||
@Query("select sum(coffee.cost * cart.count) from cart JOIN coffee on coffee.uid = cart.coffee_id and cart.count > 0")
|
@Query("select coffee.* from cart join coffee on coffee.coffee_id = cart.coffee_id and cart.count > 0 collate nocase")
|
||||||
|
suspend fun getAllInCartList(): List<Coffee>
|
||||||
|
|
||||||
|
@Query("select sum(coffee.cost * cart.count) from cart JOIN coffee on coffee.coffee_id = cart.coffee_id and cart.count > 0")
|
||||||
fun getSumInCart(): Double
|
fun getSumInCart(): Double
|
||||||
|
|
||||||
@Query("select cart.count from cart JOIN coffee on coffee.uid = cart.coffee_id where coffee.uid = :coffeeId")
|
@Query("select cart.count from cart JOIN coffee on coffee.coffee_id = cart.coffee_id where coffee.coffee_id = :coffeeId")
|
||||||
fun getCountForCoffee(coffeeId: Int): Double
|
fun getCountForCoffee(coffeeId: Int): Int
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(cart: Cart)
|
suspend fun insert(cart: Cart)
|
||||||
|
@ -13,7 +13,7 @@ interface CoffeeDao {
|
|||||||
@Query("select * from coffee order by name collate nocase asc")
|
@Query("select * from coffee order by name collate nocase asc")
|
||||||
fun getAllCoffees(): PagingSource<Int, Coffee>
|
fun getAllCoffees(): PagingSource<Int, Coffee>
|
||||||
|
|
||||||
@Query("select coffee.uid, name, cost, ingredients from coffee where coffee.uid = :uid")
|
@Query("select coffee.coffee_id, name, cost, ingredients from coffee where coffee.coffee_id = :uid")
|
||||||
suspend fun getByUid(uid: Int): Coffee?
|
suspend fun getByUid(uid: Int): Coffee?
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Update
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface OrderDao {
|
||||||
|
@Query("select * from `order` order by order_id collate nocase asc")
|
||||||
|
fun getAll(): PagingSource<Int, Order>
|
||||||
|
|
||||||
|
@Query("select * from `order` where `order`.order_id = :uid")
|
||||||
|
suspend fun getByUid(uid: Int): Order?
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT `order`.* FROM `order`
|
||||||
|
WHERE user_id = :userId
|
||||||
|
""")
|
||||||
|
fun getOrdersByUser(userId: Int): PagingSource<Int, Order>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT coffee.* FROM coffee
|
||||||
|
INNER JOIN ordercoffeecrossref ON coffee.coffee_id = ordercoffeecrossref.coffee_id
|
||||||
|
WHERE ordercoffeecrossref.order_id = :orderId
|
||||||
|
""")
|
||||||
|
fun getCoffeesByOrder(orderId: Int): Flow<List<Coffee>>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT `order`.* FROM `order`
|
||||||
|
INNER JOIN ordercoffeecrossref ON `order`.order_id = ordercoffeecrossref.order_id
|
||||||
|
WHERE date >= :startDate and date <= :endDate
|
||||||
|
""")
|
||||||
|
fun getOrdersByDate(startDate: String, endDate: String): Flow<List<OrderWithCoffees>>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(order: Order): Long
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
suspend fun insert(vararg order: Order)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(order: Order): Int
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
suspend fun delete(order: Order)
|
||||||
|
|
||||||
|
@Query("delete from `order`")
|
||||||
|
suspend fun deleteAll()
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface OrderWithCoffees {
|
||||||
|
@Query("select * from ordercoffeecrossref")
|
||||||
|
fun getAll(): List<OrderCoffeeCrossRef>
|
||||||
|
@Insert
|
||||||
|
suspend fun insert(vararg orderCoffee: OrderCoffeeCrossRef)
|
||||||
|
@Delete
|
||||||
|
suspend fun delete(orderCoffee: OrderCoffeeCrossRef)
|
||||||
|
}
|
@ -16,7 +16,7 @@ interface UserDao {
|
|||||||
@Query("select * from user where login = :login and password = :password")
|
@Query("select * from user where login = :login and password = :password")
|
||||||
suspend fun tryLogin(login: String, password: String): User?
|
suspend fun tryLogin(login: String, password: String): User?
|
||||||
|
|
||||||
@Query("select * from user where uid = :uid")
|
@Query("select * from user where user_id = :uid")
|
||||||
suspend fun getByUid(uid: Int): User?
|
suspend fun getByUid(uid: Int): User?
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
|
@ -4,23 +4,26 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "cart", foreignKeys = [
|
//@Entity(tableName = "cart", foreignKeys = [
|
||||||
ForeignKey(
|
// ForeignKey(
|
||||||
entity = Cart::class,
|
// entity = Coffee::class,
|
||||||
parentColumns = ["uid"],
|
// parentColumns = arrayOf("coffee_id"),
|
||||||
childColumns = ["coffee_id"],
|
// childColumns = arrayOf("coffee_id"),
|
||||||
onDelete = ForeignKey.RESTRICT,
|
// onDelete = ForeignKey.SET_DEFAULT,
|
||||||
onUpdate = ForeignKey.RESTRICT
|
// onUpdate = ForeignKey.CASCADE
|
||||||
)
|
// )
|
||||||
])
|
//])
|
||||||
|
@Entity(tableName = "cart")
|
||||||
data class Cart(
|
data class Cart(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val uid: Int = 0,
|
@ColumnInfo(name = "cart_id")
|
||||||
|
val cartId: Int = 0,
|
||||||
@ColumnInfo(name = "coffee_id", index = true)
|
@ColumnInfo(name = "coffee_id", index = true)
|
||||||
val coffeeId: Int,
|
val coffeeId: Int,
|
||||||
@ColumnInfo(name = "count", index = true)
|
@ColumnInfo(name = "count")
|
||||||
val count: Int = 0
|
val count: Int = 0
|
||||||
) {
|
) {
|
||||||
@Ignore
|
@Ignore
|
||||||
@ -33,13 +36,13 @@ data class Cart(
|
|||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
other as Cart
|
other as Cart
|
||||||
if (uid != other.uid) return false
|
if (cartId != other.cartId) return false
|
||||||
if (coffeeId != other.coffeeId) return false
|
if (coffeeId != other.coffeeId) return false
|
||||||
return count == other.count
|
return count == other.count
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = uid
|
var result = cartId
|
||||||
result = 31 * result + coffeeId.hashCode()
|
result = 31 * result + coffeeId.hashCode()
|
||||||
result = 31 * result + count.hashCode()
|
result = 31 * result + count.hashCode()
|
||||||
return result
|
return result
|
||||||
|
@ -8,7 +8,8 @@ import androidx.room.PrimaryKey
|
|||||||
@Entity(tableName = "coffee")
|
@Entity(tableName = "coffee")
|
||||||
data class Coffee(
|
data class Coffee(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val uid: Int = 0,
|
@ColumnInfo(name = "coffee_id")
|
||||||
|
val coffeeId: Int = 0,
|
||||||
@ColumnInfo(name = "name")
|
@ColumnInfo(name = "name")
|
||||||
var name: String,
|
var name: String,
|
||||||
@ColumnInfo(name = "cost")
|
@ColumnInfo(name = "cost")
|
||||||
@ -38,14 +39,14 @@ data class Coffee(
|
|||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
other as Coffee
|
other as Coffee
|
||||||
if (uid != other.uid) return false
|
if (coffeeId != other.coffeeId) return false
|
||||||
if (name != other.name) return false
|
if (name != other.name) return false
|
||||||
if (cost != other.cost) return false
|
if (cost != other.cost) return false
|
||||||
return ingredients == other.ingredients
|
return ingredients == other.ingredients
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = uid
|
var result = coffeeId
|
||||||
result = 31 * result + name.hashCode()
|
result = 31 * result + name.hashCode()
|
||||||
result = 31 * result + cost.hashCode()
|
result = 31 * result + cost.hashCode()
|
||||||
result = 31 * result + ingredients.hashCode()
|
result = 31 * result + ingredients.hashCode()
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.Ignore
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "order",
|
||||||
|
foreignKeys = [ForeignKey(
|
||||||
|
entity = User::class,
|
||||||
|
parentColumns = arrayOf("user_id"),
|
||||||
|
childColumns = arrayOf("user_id"),
|
||||||
|
onDelete = ForeignKey.RESTRICT,
|
||||||
|
onUpdate = ForeignKey.RESTRICT
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
data class Order(
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ColumnInfo(name = "order_id")
|
||||||
|
val orderId: Int = 0,
|
||||||
|
@ColumnInfo(name = "date")
|
||||||
|
var date: String,
|
||||||
|
@ColumnInfo(name = "user_id", index = true)
|
||||||
|
val userId: Int,
|
||||||
|
@ColumnInfo(name = "sum")
|
||||||
|
val sum: Double
|
||||||
|
) {
|
||||||
|
@Ignore
|
||||||
|
constructor(
|
||||||
|
date: String,
|
||||||
|
userId: Int,
|
||||||
|
sum: Double
|
||||||
|
) : this(0, date, userId, sum)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getOrder(index: Int = 0): Order {
|
||||||
|
return Order(
|
||||||
|
index,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
other as Order
|
||||||
|
if (orderId != other.orderId) return false
|
||||||
|
if (date != other.date) return false
|
||||||
|
if (userId != other.userId) return false
|
||||||
|
return sum == other.sum
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = orderId
|
||||||
|
result = 31 * result + date.hashCode()
|
||||||
|
result = 31 * result + userId.hashCode()
|
||||||
|
result = 31 * result + sum.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
|
||||||
|
@Entity(primaryKeys = ["order_id", "coffee_id"])
|
||||||
|
class OrderCoffeeCrossRef (
|
||||||
|
@ColumnInfo(name = "order_id")
|
||||||
|
val orderId: Int,
|
||||||
|
@ColumnInfo(name = "coffee_id")
|
||||||
|
val coffeeId: Int,
|
||||||
|
@ColumnInfo(name = "count")
|
||||||
|
val count: Int
|
||||||
|
)
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.model
|
||||||
|
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Junction
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class OrderWithCoffees(
|
||||||
|
@Embedded val order: Order,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "order_id",
|
||||||
|
entityColumn = "user_id"
|
||||||
|
)
|
||||||
|
val user: User,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "order_id",
|
||||||
|
entityColumn = "coffee_id",
|
||||||
|
associateBy = Junction(OrderCoffeeCrossRef::class)
|
||||||
|
)
|
||||||
|
val coffees: List<Coffee>,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "order_id",
|
||||||
|
entityColumn = "coffee_id",
|
||||||
|
entity = OrderCoffeeCrossRef::class,
|
||||||
|
projection = ["count"]
|
||||||
|
)
|
||||||
|
val counts: List<Int>
|
||||||
|
)
|
@ -7,7 +7,8 @@ import androidx.room.TypeConverters
|
|||||||
|
|
||||||
enum class RemoteKeyType(private val type: String) {
|
enum class RemoteKeyType(private val type: String) {
|
||||||
COFFEE(Coffee::class.simpleName ?: "Coffee"),
|
COFFEE(Coffee::class.simpleName ?: "Coffee"),
|
||||||
USER(User::class.simpleName ?: "User");
|
USER(User::class.simpleName ?: "User"),
|
||||||
|
ORDER(Order::class.simpleName ?: "Order");
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun toRemoteKeyType(value: String) = entries.first { it.type == value }
|
fun toRemoteKeyType(value: String) = entries.first { it.type == value }
|
||||||
|
@ -8,7 +8,8 @@ import androidx.room.PrimaryKey
|
|||||||
@Entity(tableName = "user")
|
@Entity(tableName = "user")
|
||||||
data class User(
|
data class User(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val uid: Int = 0,
|
@ColumnInfo(name = "user_id")
|
||||||
|
val userId: Int = 0,
|
||||||
@ColumnInfo(name = "login")
|
@ColumnInfo(name = "login")
|
||||||
var login: String,
|
var login: String,
|
||||||
@ColumnInfo(name = "fio")
|
@ColumnInfo(name = "fio")
|
||||||
@ -46,7 +47,7 @@ data class User(
|
|||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
other as User
|
other as User
|
||||||
if (uid != other.uid) return false
|
if (userId != other.userId) return false
|
||||||
if (login != other.login) return false
|
if (login != other.login) return false
|
||||||
if (fio != other.fio) return false
|
if (fio != other.fio) return false
|
||||||
if (phone != other.phone) return false
|
if (phone != other.phone) return false
|
||||||
@ -55,7 +56,7 @@ data class User(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = uid
|
var result = userId
|
||||||
result = 31 * result + login.hashCode()
|
result = 31 * result + login.hashCode()
|
||||||
result = 31 * result + fio.hashCode()
|
result = 31 * result + fio.hashCode()
|
||||||
result = 31 * result + phone.hashCode()
|
result = 31 * result + phone.hashCode()
|
||||||
|
@ -9,10 +9,11 @@ interface CartRepository {
|
|||||||
suspend fun getAll(): Cart
|
suspend fun getAll(): Cart
|
||||||
suspend fun insert(cart: Cart)
|
suspend fun insert(cart: Cart)
|
||||||
fun getAllInCart(): Flow<PagingData<Coffee>>
|
fun getAllInCart(): Flow<PagingData<Coffee>>
|
||||||
|
suspend fun getAllInCartList(): List<Coffee>
|
||||||
fun getSumInCart(): Double
|
fun getSumInCart(): Double
|
||||||
suspend fun insertCoffee(coffeeId: Int, count: Int)
|
suspend fun insertCoffee(coffeeId: Int, count: Int)
|
||||||
suspend fun deleteCoffee(coffeeId: Int, count: Int)
|
suspend fun deleteCoffee(coffeeId: Int, count: Int)
|
||||||
fun getCountForCoffee(coffeeId: Int): Double
|
fun getCountForCoffee(coffeeId: Int): Int
|
||||||
suspend fun update(cart: Cart)
|
suspend fun update(cart: Cart)
|
||||||
suspend fun deleteAll()
|
suspend fun deleteAll()
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,14 @@ class OfflineCartRepository(private val cartDao: CartDao) : CartRepository {
|
|||||||
),
|
),
|
||||||
pagingSourceFactory = cartDao::getAllInCart
|
pagingSourceFactory = cartDao::getAllInCart
|
||||||
).flow
|
).flow
|
||||||
|
|
||||||
|
override suspend fun getAllInCartList(): List<Coffee> = cartDao.getAllInCartList()
|
||||||
override fun getSumInCart(): Double = cartDao.getSumInCart()
|
override fun getSumInCart(): Double = cartDao.getSumInCart()
|
||||||
override suspend fun getAll(): Cart = cartDao.getAll()
|
override suspend fun getAll(): Cart = cartDao.getAll()
|
||||||
override suspend fun insert(cart: Cart) = cartDao.insert(cart)
|
override suspend fun insert(cart: Cart) = cartDao.insert(cart)
|
||||||
override suspend fun insertCoffee(coffeeId: Int, count: Int) = cartDao.insertCoffee(coffeeId, count)
|
override suspend fun insertCoffee(coffeeId: Int, count: Int) = cartDao.insertCoffee(coffeeId, count)
|
||||||
override suspend fun deleteCoffee(coffeeId: Int, count: Int) = cartDao.deleteCoffee(coffeeId, count)
|
override suspend fun deleteCoffee(coffeeId: Int, count: Int) = cartDao.deleteCoffee(coffeeId, count)
|
||||||
override fun getCountForCoffee(coffeeId: Int): Double = cartDao.getCountForCoffee(coffeeId)
|
override fun getCountForCoffee(coffeeId: Int): Int = cartDao.getCountForCoffee(coffeeId)
|
||||||
override suspend fun update(cart: Cart) = cartDao.update(cart)
|
override suspend fun update(cart: Cart) = cartDao.update(cart)
|
||||||
override suspend fun deleteAll() = cartDao.deleteAll()
|
override suspend fun deleteAll() = cartDao.deleteAll()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.OrderDao
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class OfflineOrderRepository(private val orderDao: OrderDao) : OrderRepository {
|
||||||
|
override fun getOrdersByUser(id: Int): Flow<PagingData<Order>> = Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
pagingSourceFactory = { orderDao.getOrdersByUser(id) }
|
||||||
|
).flow
|
||||||
|
override suspend fun getCoffeesByOrder(orderId: Int): Flow<List<Coffee>> = orderDao.getCoffeesByOrder(orderId)
|
||||||
|
override suspend fun insert(order: Order) = orderDao.insert(order)
|
||||||
|
override suspend fun getOrdersByDate(
|
||||||
|
startDate: String,
|
||||||
|
endDate: String
|
||||||
|
): Flow<List<OrderWithCoffees>> = orderDao.getOrdersByDate(startDate, endDate)
|
||||||
|
fun getOrdersByUserPagingSource(id: Int): PagingSource<Int, Order> = orderDao.getOrdersByUser(id)
|
||||||
|
suspend fun deleteAll() = orderDao.deleteAll()
|
||||||
|
suspend fun insertOrders(orders: List<Order>) =
|
||||||
|
orderDao.insert(*orders.toTypedArray())
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.OrderWithCoffees
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
|
|
||||||
|
class OfflineOrderWithCoffeesRepository(private val orderCoffeesDAO: OrderWithCoffees) : OrderWithCoffeesRepository {
|
||||||
|
override suspend fun getAll() = orderCoffeesDAO.getAll()
|
||||||
|
override suspend fun insert(orderCoffee: OrderCoffeeCrossRef) = orderCoffeesDAO.insert(orderCoffee)
|
||||||
|
override suspend fun delete(orderCoffee: OrderCoffeeCrossRef) = orderCoffeesDAO.delete(orderCoffee)
|
||||||
|
suspend fun insertAll(orderBouquet: List<OrderCoffeeCrossRef>) =
|
||||||
|
orderCoffeesDAO.insert(*orderBouquet.toTypedArray())
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface OrderRepository {
|
||||||
|
suspend fun getCoffeesByOrder(orderId: Int): Flow<List<Coffee>>
|
||||||
|
fun getOrdersByUser(id: Int): Flow<PagingData<Order>>
|
||||||
|
suspend fun insert(order: Order): Long
|
||||||
|
suspend fun getOrdersByDate(startDate: String, endDate: String): Flow<List<OrderWithCoffees>>
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
|
|
||||||
|
interface OrderWithCoffeesRepository {
|
||||||
|
suspend fun getAll(): List<OrderCoffeeCrossRef>
|
||||||
|
suspend fun insert(orderCoffee: OrderCoffeeCrossRef)
|
||||||
|
suspend fun delete(orderCoffee: OrderCoffeeCrossRef)
|
||||||
|
}
|
@ -8,8 +8,10 @@ import com.zyzf.coffeepreorder.CoffeeApplication
|
|||||||
import com.zyzf.coffeepreorder.ui.cart.CartViewModel
|
import com.zyzf.coffeepreorder.ui.cart.CartViewModel
|
||||||
import com.zyzf.coffeepreorder.ui.coffee.CoffeeListViewModel
|
import com.zyzf.coffeepreorder.ui.coffee.CoffeeListViewModel
|
||||||
import com.zyzf.coffeepreorder.ui.login.LoginViewModel
|
import com.zyzf.coffeepreorder.ui.login.LoginViewModel
|
||||||
|
import com.zyzf.coffeepreorder.ui.order.OrderViewModel
|
||||||
import com.zyzf.coffeepreorder.ui.profile.ProfileViewModel
|
import com.zyzf.coffeepreorder.ui.profile.ProfileViewModel
|
||||||
import com.zyzf.coffeepreorder.ui.register.RegisterViewModel
|
import com.zyzf.coffeepreorder.ui.register.RegisterViewModel
|
||||||
|
import com.zyzf.coffeepreorder.ui.statistic.OrderStatisticViewModel
|
||||||
|
|
||||||
object AppViewModelProvider {
|
object AppViewModelProvider {
|
||||||
val Factory = viewModelFactory {
|
val Factory = viewModelFactory {
|
||||||
@ -26,7 +28,16 @@ object AppViewModelProvider {
|
|||||||
RegisterViewModel(coffeeApplication().container.userRestRepository)
|
RegisterViewModel(coffeeApplication().container.userRestRepository)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
ProfileViewModel(coffeeApplication().container.userRestRepository)
|
ProfileViewModel(coffeeApplication().container.userRestRepository,
|
||||||
|
coffeeApplication().container.cartRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
OrderStatisticViewModel(coffeeApplication().container.orderRestRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
OrderViewModel(coffeeApplication().container.orderRestRepository,
|
||||||
|
coffeeApplication().container.orderCoffeesRestRepository,
|
||||||
|
coffeeApplication().container.cartRepository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ fun Cart(
|
|||||||
)
|
)
|
||||||
CartList(
|
CartList(
|
||||||
coffeeList = coffeeListUiState,
|
coffeeList = coffeeListUiState,
|
||||||
onDeleteFromCartClick = {currentCoffee: Coffee ->
|
onDeleteFromCartClick = { currentCoffee: Coffee ->
|
||||||
coffee.value = currentCoffee
|
coffee.value = currentCoffee
|
||||||
openDialog.value = true
|
openDialog.value = true
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ fun Cart(
|
|||||||
private fun CartList(
|
private fun CartList(
|
||||||
coffeeList: LazyPagingItems<Coffee>,
|
coffeeList: LazyPagingItems<Coffee>,
|
||||||
onDeleteFromCartClick: (coffee: Coffee) -> Unit,
|
onDeleteFromCartClick: (coffee: Coffee) -> Unit,
|
||||||
getCoffeeCount: (coffeeId: Int) -> Double
|
getCoffeeCount: (Int) -> Int
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
@ -151,7 +151,7 @@ private fun CartList(
|
|||||||
private fun CartListItem (
|
private fun CartListItem (
|
||||||
coffee: Coffee,
|
coffee: Coffee,
|
||||||
onDeleteFromCartClick: (coffee: Coffee) -> Unit,
|
onDeleteFromCartClick: (coffee: Coffee) -> Unit,
|
||||||
getCoffeeCount: (coffeeId: Int) -> Double
|
getCoffeeCount: (coffeeId: Int) -> Int
|
||||||
) {
|
) {
|
||||||
Row(modifier = Modifier
|
Row(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -159,7 +159,7 @@ private fun CartListItem (
|
|||||||
.padding(bottom = 10.dp, top = 10.dp),
|
.padding(bottom = 10.dp, top = 10.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceAround) {
|
horizontalArrangement = Arrangement.SpaceAround) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png")
|
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.coffeeId +".png")
|
||||||
.crossfade(true).build(),
|
.crossfade(true).build(),
|
||||||
error = painterResource(R.drawable.ic_broken_image),
|
error = painterResource(R.drawable.ic_broken_image),
|
||||||
placeholder = painterResource(R.drawable.loading_img),
|
placeholder = painterResource(R.drawable.loading_img),
|
||||||
@ -173,7 +173,7 @@ private fun CartListItem (
|
|||||||
Modifier
|
Modifier
|
||||||
.weight(2f)
|
.weight(2f)
|
||||||
.padding(start = 20.dp)) {
|
.padding(start = 20.dp)) {
|
||||||
val coffeeCount: Double = getCoffeeCount(coffee.uid)
|
val coffeeCount: Int = getCoffeeCount(coffee.coffeeId)
|
||||||
val currentCoffeeName: String = if (coffeeCount > 1) {
|
val currentCoffeeName: String = if (coffeeCount > 1) {
|
||||||
coffee.name + " x" + coffeeCount.toString()
|
coffee.name + " x" + coffeeCount.toString()
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,11 +11,11 @@ class CartViewModel(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val coffeeListUiState: Flow<PagingData<Coffee>> = cartRepository.getAllInCart()
|
val coffeeListUiState: Flow<PagingData<Coffee>> = cartRepository.getAllInCart()
|
||||||
|
|
||||||
fun getCountForCoffee(coffeeId: Int): Double {
|
fun getCountForCoffee(coffeeId: Int): Int {
|
||||||
return cartRepository.getCountForCoffee(coffeeId)
|
return cartRepository.getCountForCoffee(coffeeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteCoffeeFromCart(coffee: Coffee) {
|
suspend fun deleteCoffeeFromCart(coffee: Coffee) {
|
||||||
cartRepository.deleteCoffee(coffee.uid, 1)
|
cartRepository.deleteCoffee(coffee.coffeeId, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -180,7 +180,7 @@ fun CoffeeList(
|
|||||||
imageUri = imageUri,
|
imageUri = imageUri,
|
||||||
beforeOpen = {coffeeUid: Int ->
|
beforeOpen = {coffeeUid: Int ->
|
||||||
if (coffeeUid != 0 && !isImageChanged.value) {
|
if (coffeeUid != 0 && !isImageChanged.value) {
|
||||||
imageUri = Uri.parse("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.value.uid +".png")
|
imageUri = Uri.parse("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.value.coffeeId +".png")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -199,7 +199,7 @@ private fun AddEditModalBottomSheet(
|
|||||||
imageUri: Any?,
|
imageUri: Any?,
|
||||||
beforeOpen: (coffeeUid: Int) -> Unit
|
beforeOpen: (coffeeUid: Int) -> Unit
|
||||||
) {
|
) {
|
||||||
beforeOpen(coffee.value.uid)
|
beforeOpen(coffee.value.coffeeId)
|
||||||
var name: String by remember { mutableStateOf("")}
|
var name: String by remember { mutableStateOf("")}
|
||||||
var cost: Double by remember { mutableDoubleStateOf(0.0) }
|
var cost: Double by remember { mutableDoubleStateOf(0.0) }
|
||||||
var ingredients: String by remember { mutableStateOf("")}
|
var ingredients: String by remember { mutableStateOf("")}
|
||||||
@ -271,16 +271,16 @@ private fun AddEditModalBottomSheet(
|
|||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||||
if (coffee.value.uid == 0) {
|
if (coffee.value.coffeeId == 0) {
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
onAddClick(Coffee(coffee.value.uid, name, cost, ingredients), context)
|
onAddClick(Coffee(coffee.value.coffeeId, name, cost, ingredients), context)
|
||||||
openDialog.value = false
|
openDialog.value = false
|
||||||
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
||||||
Text("Добавить")
|
Text("Добавить")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
onEditClick(Coffee(coffee.value.uid, name, cost, ingredients), context)
|
onEditClick(Coffee(coffee.value.coffeeId, name, cost, ingredients), context)
|
||||||
openDialog.value = false
|
openDialog.value = false
|
||||||
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
||||||
Text("Изменить")
|
Text("Изменить")
|
||||||
@ -339,7 +339,7 @@ private fun CoffeeListItem(
|
|||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest
|
model = ImageRequest
|
||||||
.Builder(context = LocalContext.current)
|
.Builder(context = LocalContext.current)
|
||||||
.data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png")
|
.data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.coffeeId +".png")
|
||||||
.crossfade(true).build(),
|
.crossfade(true).build(),
|
||||||
error = painterResource(R.drawable.ic_broken_image),
|
error = painterResource(R.drawable.ic_broken_image),
|
||||||
placeholder = painterResource(R.drawable.loading_img),
|
placeholder = painterResource(R.drawable.loading_img),
|
||||||
@ -363,7 +363,7 @@ private fun CoffeeListItem(
|
|||||||
.padding(top = 5.dp)) {
|
.padding(top = 5.dp)) {
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
onAddToCartClick(coffee.uid)
|
onAddToCartClick(coffee.coffeeId)
|
||||||
},
|
},
|
||||||
shape = CircleShape,
|
shape = CircleShape,
|
||||||
modifier = Modifier.fillMaxWidth(fraction = 0.75f)
|
modifier = Modifier.fillMaxWidth(fraction = 0.75f)
|
||||||
|
@ -55,6 +55,7 @@ class CoffeeListViewModel(
|
|||||||
suspend fun editCoffee(coffee: Coffee, imageUri: Any?, context: Context) {
|
suspend fun editCoffee(coffee: Coffee, imageUri: Any?, context: Context) {
|
||||||
val editedCoffee: Int = coffeeRepository.update(coffee)
|
val editedCoffee: Int = coffeeRepository.update(coffee)
|
||||||
if (imageUri !is Uri) return
|
if (imageUri !is Uri) return
|
||||||
|
if (imageUri.host == "zyzf.space") return
|
||||||
val inputStream = context.contentResolver.openInputStream(imageUri)
|
val inputStream = context.contentResolver.openInputStream(imageUri)
|
||||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.zyzf.coffeepreorder.ui.login
|
package com.zyzf.coffeepreorder.ui.login
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@ -16,7 +15,6 @@ import androidx.compose.material3.ButtonDefaults
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@ -33,7 +31,6 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -45,8 +42,6 @@ import com.zyzf.coffeepreorder.database.AppDatabase
|
|||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -142,17 +137,4 @@ fun Login(
|
|||||||
Text(text = "Зарегистрироваться")
|
Text(text = "Зарегистрироваться")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
|
||||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
@Composable
|
|
||||||
fun LoginPreview() {
|
|
||||||
CoffeePreorderTheme {
|
|
||||||
Surface(
|
|
||||||
color = MaterialTheme.colorScheme.background
|
|
||||||
) {
|
|
||||||
Login(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ class LoginViewModel(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
suspend fun tryLogin(login: String, password: String): User? {
|
suspend fun tryLogin(login: String, password: String): User? {
|
||||||
val user: User? = userRepository.tryLogin(login, password)
|
val user: User? = userRepository.tryLogin(login, password)
|
||||||
return if (user?.uid == 0) {
|
return if (user?.userId == 0) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
user
|
user
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.zyzf.coffeepreorder.ui.navigation
|
package com.zyzf.coffeepreorder.ui.navigation
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -26,6 +28,7 @@ import androidx.navigation.compose.NavHost
|
|||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.ui.cart.Cart
|
import com.zyzf.coffeepreorder.ui.cart.Cart
|
||||||
import com.zyzf.coffeepreorder.ui.coffee.CoffeeList
|
import com.zyzf.coffeepreorder.ui.coffee.CoffeeList
|
||||||
@ -33,6 +36,7 @@ import com.zyzf.coffeepreorder.ui.login.Login
|
|||||||
import com.zyzf.coffeepreorder.ui.order.Order
|
import com.zyzf.coffeepreorder.ui.order.Order
|
||||||
import com.zyzf.coffeepreorder.ui.profile.Profile
|
import com.zyzf.coffeepreorder.ui.profile.Profile
|
||||||
import com.zyzf.coffeepreorder.ui.register.Register
|
import com.zyzf.coffeepreorder.ui.register.Register
|
||||||
|
import com.zyzf.coffeepreorder.ui.statistic.OrderStatistic
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -74,6 +78,11 @@ fun Navbar(
|
|||||||
) {
|
) {
|
||||||
NavigationBar(modifier) {
|
NavigationBar(modifier) {
|
||||||
Screen.bottomBarItems.forEach { screen ->
|
Screen.bottomBarItems.forEach { screen ->
|
||||||
|
if (CoffeeApplication.currentUser != null &&
|
||||||
|
CoffeeApplication.currentUser!!.role != "admin" &&
|
||||||
|
screen.route == "order-statistic") {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
icon = { Icon(screen.icon, contentDescription = null) },
|
icon = { Icon(screen.icon, contentDescription = null) },
|
||||||
label = { Text(stringResource(screen.resourceId)) },
|
label = { Text(stringResource(screen.resourceId)) },
|
||||||
@ -92,6 +101,7 @@ fun Navbar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Navhost(
|
fun Navhost(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@ -109,6 +119,8 @@ fun Navhost(
|
|||||||
composable(Screen.Profile.route) { Profile(navController) }
|
composable(Screen.Profile.route) { Profile(navController) }
|
||||||
composable(Screen.Cart.route) { Cart(navController) }
|
composable(Screen.Cart.route) { Cart(navController) }
|
||||||
composable(Screen.Order.route) { Order(navController) }
|
composable(Screen.Order.route) { Order(navController) }
|
||||||
|
composable(Screen.OrderStatistic.route) { OrderStatistic() }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,13 @@ package com.zyzf.coffeepreorder.ui.navigation
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AccountCircle
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
|
import androidx.compose.material.icons.filled.DateRange
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.ShoppingCart
|
import androidx.compose.material.icons.filled.ShoppingCart
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
|
||||||
enum class Screen(
|
enum class Screen(
|
||||||
val route: String,
|
val route: String,
|
||||||
@ -32,13 +34,17 @@ enum class Screen(
|
|||||||
),
|
),
|
||||||
Order(
|
Order(
|
||||||
"order", R.string.coffee_order, Icons.Filled.ShoppingCart
|
"order", R.string.coffee_order, Icons.Filled.ShoppingCart
|
||||||
|
),
|
||||||
|
OrderStatistic(
|
||||||
|
"order-statistic", R.string.coffee_order_statistic, Icons.Filled.DateRange
|
||||||
);
|
);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val bottomBarItems = listOf(
|
val bottomBarItems = listOf(
|
||||||
CoffeeList,
|
CoffeeList,
|
||||||
Profile,
|
Profile,
|
||||||
Cart
|
Cart,
|
||||||
|
OrderStatistic
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getItem(route: String): Screen? {
|
fun getItem(route: String): Screen? {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.zyzf.coffeepreorder.ui.order
|
package com.zyzf.coffeepreorder.ui.order
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -24,7 +23,6 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.IconButtonDefaults
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TimePicker
|
import androidx.compose.material3.TimePicker
|
||||||
import androidx.compose.material3.rememberDatePickerState
|
import androidx.compose.material3.rememberDatePickerState
|
||||||
@ -33,18 +31,18 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
@ -53,18 +51,21 @@ import java.util.Locale
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Order(navController: NavController?) {
|
fun Order(
|
||||||
|
navController: NavController?,
|
||||||
|
viewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val openDatePicker = remember { mutableStateOf(false) }
|
val openDatePicker = remember { mutableStateOf(false) }
|
||||||
val openTimePicker = remember { mutableStateOf(false) }
|
val openTimePicker = remember { mutableStateOf(false) }
|
||||||
val calendar = Calendar.getInstance()
|
val calendar = Calendar.getInstance()
|
||||||
val datePickerState = rememberDatePickerState(initialSelectedDateMillis = calendar.timeInMillis)
|
val datePickerState = rememberDatePickerState(initialSelectedDateMillis = calendar.timeInMillis)
|
||||||
val timePickerState = rememberTimePickerState(is24Hour = true, initialHour = calendar.get(Calendar.HOUR_OF_DAY), initialMinute = calendar.get(Calendar.MINUTE))
|
val timePickerState = rememberTimePickerState(is24Hour = true, initialHour = calendar.get(Calendar.HOUR_OF_DAY), initialMinute = calendar.get(Calendar.MINUTE))
|
||||||
val formatter = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT)
|
val formatter = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT)
|
||||||
val context = LocalContext.current
|
|
||||||
val sum = remember { mutableStateOf(0.0) }
|
val sum = remember { mutableStateOf(0.0) }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
sum.value = AppDatabase.getInstance(context).cartDao().getSumInCart()
|
sum.value = viewModel.getSumInCart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
@ -107,7 +108,12 @@ fun Order(navController: NavController?) {
|
|||||||
}
|
}
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
ExtendedFloatingActionButton(
|
ExtendedFloatingActionButton(
|
||||||
onClick = { navController?.navigate(Screen.CoffeeList.route) },
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.createNewOrder(datePickerState.selectedDateMillis)
|
||||||
|
}
|
||||||
|
navController?.navigate(Screen.Cart.route)
|
||||||
|
},
|
||||||
icon = { Icon(Icons.Filled.Add, "Оформить заказ") },
|
icon = { Icon(Icons.Filled.Add, "Оформить заказ") },
|
||||||
text = { Text(text = "Оформить заказ") },
|
text = { Text(text = "Оформить заказ") },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -169,17 +175,4 @@ fun Order(navController: NavController?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
|
||||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
@Composable
|
|
||||||
fun OrderPreview() {
|
|
||||||
CoffeePreorderTheme {
|
|
||||||
Surface(
|
|
||||||
color = MaterialTheme.colorScheme.background
|
|
||||||
) {
|
|
||||||
Order(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.order
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderCoffeeCrossRef
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OrderRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OrderWithCoffeesRepository
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class OrderViewModel(
|
||||||
|
private val orderRepository: OrderRepository,
|
||||||
|
private val orderWithCoffeesRepository: OrderWithCoffeesRepository,
|
||||||
|
private val cartRepository: CartRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
fun getSumInCart(): Double {
|
||||||
|
return cartRepository.getSumInCart()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun createNewOrder(date: Long?) {
|
||||||
|
val coffees: List<Coffee> = cartRepository.getAllInCartList()
|
||||||
|
if (coffees.isEmpty() || date == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val cartSum: Double = cartRepository.getSumInCart()
|
||||||
|
val formatter = SimpleDateFormat("dd-MM-yyyy", Locale.ROOT)
|
||||||
|
val currentOrderId: Long = orderRepository.insert(Order(formatter.format(Date(date)), CoffeeApplication.currentUser!!.userId, cartSum))
|
||||||
|
coffees.forEach { coffee: Coffee ->
|
||||||
|
orderWithCoffeesRepository.insert(OrderCoffeeCrossRef(currentOrderId.toInt(), coffee.coffeeId, cartRepository.getCountForCoffee(coffee.coffeeId)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,10 +47,8 @@ import com.zyzf.coffeepreorder.R
|
|||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
import com.zyzf.coffeepreorder.ui.login.LoginViewModel
|
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@ -204,7 +202,7 @@ fun Profile(
|
|||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val user: User = viewModel.changeUser(user.password, userOldPsswd.value, userNewPsswd.value, userNewPsswd.value,
|
val user: User = viewModel.changeUser(user.password, userOldPsswd.value, userNewPsswd.value, userNewPsswd.value,
|
||||||
user.uid, userLogin, userFIO, userPhone, user.role)
|
user.userId, userLogin, userFIO, userPhone, user.role)
|
||||||
CoffeeApplication.currentUser = user
|
CoffeeApplication.currentUser = user
|
||||||
}
|
}
|
||||||
openDialogEdit.value = false
|
openDialogEdit.value = false
|
||||||
@ -241,8 +239,8 @@ fun Profile(
|
|||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
CoffeeApplication.currentUser = null
|
viewModel.logout()
|
||||||
}
|
}
|
||||||
openDialogExit.value = false
|
openDialogExit.value = false
|
||||||
navController?.navigate(Screen.Login.route)
|
navController?.navigate(Screen.Login.route)
|
||||||
@ -279,9 +277,8 @@ fun Profile(
|
|||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
CoffeeApplication.currentUser = null
|
viewModel.deleteAccount(user)
|
||||||
AppDatabase.getInstance(context).userDao().delete(user)
|
|
||||||
}
|
}
|
||||||
openDialogDelete.value = false
|
openDialogDelete.value = false
|
||||||
navController?.navigate(Screen.Login.route)
|
navController?.navigate(Screen.Login.route)
|
||||||
|
@ -2,12 +2,13 @@ package com.zyzf.coffeepreorder.ui.profile
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.zyzf.coffeepreorder.CoffeeApplication
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
||||||
|
|
||||||
class ProfileViewModel(
|
class ProfileViewModel(
|
||||||
private val userRepository: UserRepository
|
private val userRepository: UserRepository,
|
||||||
|
private val cartRepository: CartRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
suspend fun changeUser(currentUserPassw: String,
|
suspend fun changeUser(currentUserPassw: String,
|
||||||
userOldPsswd: String,
|
userOldPsswd: String,
|
||||||
@ -25,6 +26,16 @@ class ProfileViewModel(
|
|||||||
val userUid: Int? = userRepository.update(User(
|
val userUid: Int? = userRepository.update(User(
|
||||||
userUid, userLogin, userFIO, userPhone, currentUserPassw, userRole))
|
userUid, userLogin, userFIO, userPhone, currentUserPassw, userRole))
|
||||||
}
|
}
|
||||||
return userRepository.getByUid(userUid!!)!!
|
return userRepository.getByUid(userUid)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun logout() {
|
||||||
|
CoffeeApplication.currentUser = null
|
||||||
|
cartRepository.deleteAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteAccount(user: User) {
|
||||||
|
logout()
|
||||||
|
userRepository.delete(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,6 @@ import com.zyzf.coffeepreorder.database.model.User
|
|||||||
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@ -0,0 +1,281 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.statistic
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.DatePicker
|
||||||
|
import androidx.compose.material3.DatePickerState
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberDatePickerState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun OrderStatistic(
|
||||||
|
viewModel: OrderStatisticViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val openDatePicker = remember { mutableStateOf(false) }
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
val datePickerState = rememberDatePickerState(initialSelectedDateMillis = calendar.timeInMillis)
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val orderListUiState = viewModel.orderListUiState.collectAsState(initial = listOf())
|
||||||
|
val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")
|
||||||
|
val startDate = remember { mutableStateOf(LocalDate.now().minusDays(1).format(formatter))}
|
||||||
|
val endDate = remember { mutableStateOf(LocalDate.now().format(formatter))}
|
||||||
|
val isStartDate = remember { mutableStateOf(true)}
|
||||||
|
Scaffold(
|
||||||
|
topBar = {},
|
||||||
|
floatingActionButton = {
|
||||||
|
if (CoffeeApplication.currentUser?.role == "admin") {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.getOrdersByDate(startDate.value, endDate.value, coroutineScope)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Modifier
|
||||||
|
.padding(all = 20.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Check,
|
||||||
|
contentDescription = "Perform",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column (modifier = Modifier
|
||||||
|
.padding(innerPadding).fillMaxSize()) {
|
||||||
|
StartEndDateBlock(
|
||||||
|
startDate = startDate,
|
||||||
|
endDate = endDate,
|
||||||
|
onStartDateClick = {
|
||||||
|
isStartDate.value = true
|
||||||
|
openDatePicker.value = true
|
||||||
|
},
|
||||||
|
onEndDateClick = {
|
||||||
|
isStartDate.value = false
|
||||||
|
openDatePicker.value = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
TotalBlock(orderList = orderListUiState.value)
|
||||||
|
OrderList(
|
||||||
|
orderList = orderListUiState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DateDialog(
|
||||||
|
startDate = startDate,
|
||||||
|
endDate = endDate,
|
||||||
|
isStartDate = isStartDate,
|
||||||
|
openDatePicker = openDatePicker,
|
||||||
|
datePickerState = datePickerState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OrderList(
|
||||||
|
orderList: State<List<OrderWithCoffees>>,
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.padding(start = 10.dp, end = 10.dp)) {
|
||||||
|
items(items = orderList.value) {order ->
|
||||||
|
OrderListItem(
|
||||||
|
order = order
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun StartEndDateBlock(
|
||||||
|
startDate: MutableState<String>,
|
||||||
|
endDate: MutableState<String>,
|
||||||
|
onStartDateClick: () -> Unit,
|
||||||
|
onEndDateClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(max = 140.dp)
|
||||||
|
.padding(bottom = 10.dp, top = 10.dp)) {
|
||||||
|
|
||||||
|
Row(horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Button(onClick = {onStartDateClick()},
|
||||||
|
shape = CircleShape,
|
||||||
|
modifier = Modifier.width(100.dp).padding(10.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
contentColor = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(text = "От")
|
||||||
|
}
|
||||||
|
Text(text = startDate.value, modifier = Modifier.padding(10.dp))
|
||||||
|
Button(onClick = {onEndDateClick()},
|
||||||
|
shape = CircleShape,
|
||||||
|
modifier = Modifier.width(100.dp).padding(10.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
contentColor = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(text = "До")
|
||||||
|
}
|
||||||
|
Text(text = endDate.value, modifier = Modifier.padding(10.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TotalBlock(
|
||||||
|
orderList: List<OrderWithCoffees>
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(max = 140.dp)
|
||||||
|
.padding(bottom = 10.dp, top = 10.dp)) {
|
||||||
|
|
||||||
|
Row(horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Row(modifier = Modifier.width(145.dp)) {
|
||||||
|
Text(text = "Итого: ", modifier = Modifier.padding(5.dp))
|
||||||
|
Text(text = orderList.sumOf { it.order.sum }.toString(), modifier = Modifier.padding(5.dp))
|
||||||
|
}
|
||||||
|
Row(modifier = Modifier.width(145.dp)) {
|
||||||
|
Text(text = "Количество: ", modifier = Modifier.padding(5.dp))
|
||||||
|
Text(text = orderList.count().toString(), modifier = Modifier.padding(5.dp))
|
||||||
|
}
|
||||||
|
Row(modifier = Modifier.width(145.dp)) {
|
||||||
|
Text(text = "Среднее: ", modifier = Modifier.padding(5.dp))
|
||||||
|
Text(text = (orderList.sumOf { it.order.sum } / orderList.count()).toString(), modifier = Modifier.padding(5.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OrderListItem(
|
||||||
|
order: OrderWithCoffees
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(max = 150.dp)
|
||||||
|
.padding(bottom = 10.dp, top = 10.dp),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Text(text = order.user.fio, fontSize = 25.sp)
|
||||||
|
Text(text = order.user.phone, fontSize = 25.sp)
|
||||||
|
Text(text = String.format("%.2f", order.order.sum), fontSize = 30.sp)
|
||||||
|
Row() {
|
||||||
|
for (i in (0..order.coffees.count()-1)) {
|
||||||
|
if (order.counts[i] > 1) {
|
||||||
|
Text(text = order.coffees[i].name + " x" + order.counts[i] + "(" + order.coffees[i].cost * order.counts[i] + ")", fontSize = 15.sp)
|
||||||
|
} else {
|
||||||
|
Text(text = order.coffees[i].name + "(" + order.coffees[i].cost + ")", fontSize = 15.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun DateDialog(
|
||||||
|
startDate: MutableState<String>,
|
||||||
|
endDate: MutableState<String>,
|
||||||
|
isStartDate: MutableState<Boolean>,
|
||||||
|
openDatePicker: MutableState<Boolean>,
|
||||||
|
datePickerState: DatePickerState
|
||||||
|
) {
|
||||||
|
if (openDatePicker.value) {
|
||||||
|
Dialog(onDismissRequest = { openDatePicker.value = false },
|
||||||
|
properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true, usePlatformDefaultWidth = false)
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight(0.7f)
|
||||||
|
.padding(16.dp),
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
) {
|
||||||
|
Column(verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.End) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (datePickerState.selectedDateMillis != null) {
|
||||||
|
val formatter = SimpleDateFormat("dd-MM-yyyy", Locale.ROOT)
|
||||||
|
if (isStartDate.value) {
|
||||||
|
startDate.value = formatter.format(Date(datePickerState.selectedDateMillis!!))
|
||||||
|
} else {
|
||||||
|
endDate.value = formatter.format(Date(datePickerState.selectedDateMillis!!))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
openDatePicker.value = false
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(10.dp),
|
||||||
|
) {
|
||||||
|
Text("Ок")
|
||||||
|
}
|
||||||
|
DatePicker(
|
||||||
|
state = datePickerState,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(10.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.statistic
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OrderRepository
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
class OrderStatisticViewModel(
|
||||||
|
private val orderRepository: OrderRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
private val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")
|
||||||
|
private var startDate: String = LocalDate.now().minusDays(1).format(formatter)
|
||||||
|
private var endDate: String = LocalDate.now().format(formatter)
|
||||||
|
|
||||||
|
var orderListUiState: Flow<List<OrderWithCoffees>> = temp()
|
||||||
|
|
||||||
|
private fun temp(): Flow<List<OrderWithCoffees>> {
|
||||||
|
var orderWithCoffees: Flow<List<OrderWithCoffees>>? = null
|
||||||
|
runBlocking {
|
||||||
|
orderWithCoffees = orderRepository.getOrdersByDate(startDate, endDate)
|
||||||
|
}
|
||||||
|
return orderWithCoffees!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOrdersByDate(startDate: String, endDate: String, scope: CoroutineScope) {
|
||||||
|
this.startDate = startDate
|
||||||
|
this.endDate = endDate
|
||||||
|
scope.launch {
|
||||||
|
orderListUiState = orderRepository.getOrdersByDate(startDate, endDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
<string name="coffee_main_title">Меню</string>
|
<string name="coffee_main_title">Меню</string>
|
||||||
<string name="coffee_cart">Корзина</string>
|
<string name="coffee_cart">Корзина</string>
|
||||||
<string name="coffee_order">Заказ</string>
|
<string name="coffee_order">Заказ</string>
|
||||||
|
<string name="coffee_order_statistic">Статистика</string>
|
||||||
<string name="coffee_name">Название</string>
|
<string name="coffee_name">Название</string>
|
||||||
<string name="coffee_cost">Стоимость</string>
|
<string name="coffee_cost">Стоимость</string>
|
||||||
<string name="coffee_ingredients">Ингредиенты</string>
|
<string name="coffee_ingredients">Ингредиенты</string>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<network-security-config>
|
<network-security-config>
|
||||||
<domain-config cleartextTrafficPermitted="true">
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
<domain includeSubdomains="true">192.168.0.100</domain>
|
<domain includeSubdomains="true">192.168.1.230</domain>
|
||||||
</domain-config>
|
</domain-config>
|
||||||
</network-security-config>
|
</network-security-config>
|
4
backend/Dockerfile
Normal file
4
backend/Dockerfile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
FROM eclipse-temurin:17-jdk-alpine
|
||||||
|
VOLUME /tmp
|
||||||
|
COPY build/libs/yan-0.0.1-SNAPSHOT.jar app.jar
|
||||||
|
ENTRYPOINT ["java","-jar","/app.jar"]
|
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.2.0'
|
id 'org.springframework.boot' version '3.2.1'
|
||||||
id 'io.spring.dependency-management' version '1.1.4'
|
id 'io.spring.dependency-management' version '1.1.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,16 +16,15 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
implementation 'com.h2database:h2'
|
//implementation 'com.h2database:h2:2.1.212'
|
||||||
|
|
||||||
implementation 'org.hibernate.validator:hibernate-validator'
|
implementation 'org.hibernate.validator:hibernate-validator'
|
||||||
implementation 'org.projectlombok:lombok'
|
implementation 'org.projectlombok:lombok'
|
||||||
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
java.targetCompatibility = JavaVersion.VERSION_17
|
|
@ -1,7 +1,20 @@
|
|||||||
package com.kalyshev.yan.coffee.repository;
|
package com.kalyshev.yan.coffee.repository;
|
||||||
|
|
||||||
import com.kalyshev.yan.coffee.model.Coffee;
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffeeCrossRef;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.data.util.Pair;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface CoffeeRepository extends JpaRepository<Coffee, Long> {
|
public interface CoffeeRepository extends JpaRepository<Coffee, Long> {
|
||||||
|
@Query(value = """
|
||||||
|
select c, ord.count from Coffee c
|
||||||
|
join OrderCoffeeCrossRef ord on c.id = ord.coffee.id
|
||||||
|
join Order o on o.id = ord.order.id
|
||||||
|
where o.id = :order_id
|
||||||
|
""")
|
||||||
|
public List<Object[]> findCoffeesByOrder(@Param("order_id") Long orderId);
|
||||||
}
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.kalyshev.yan.order.controller;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffeeCrossRef;
|
||||||
|
|
||||||
|
public class OrderCoffeeCrossRefDto {
|
||||||
|
private Long id;
|
||||||
|
private Long orderId;
|
||||||
|
private Long coffeeId;
|
||||||
|
private int count;
|
||||||
|
public OrderCoffeeCrossRefDto(){}
|
||||||
|
public OrderCoffeeCrossRefDto(OrderCoffeeCrossRef orderCoffeeCrossRef) {
|
||||||
|
this.id = orderCoffeeCrossRef.getId();
|
||||||
|
this.orderId = orderCoffeeCrossRef.getOrder().getId();
|
||||||
|
this.coffeeId = orderCoffeeCrossRef.getCoffee().getId();
|
||||||
|
this.count = orderCoffeeCrossRef.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public Long getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
public void setOrderId(Long orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
}
|
||||||
|
public Long getCoffeeId() {
|
||||||
|
return coffeeId;
|
||||||
|
}
|
||||||
|
public void setCoffeeId(Long coffeeId) {
|
||||||
|
this.coffeeId = coffeeId;
|
||||||
|
}
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
public void setCount(int count) {
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.kalyshev.yan.order.controller;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.WebConfiguration;
|
||||||
|
import com.kalyshev.yan.coffee.controller.CoffeeDto;
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffeeCrossRef;
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffees;
|
||||||
|
import com.kalyshev.yan.order.service.OrderService;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.data.util.Pair;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(WebConfiguration.REST_API + "/order")
|
||||||
|
public class OrderController {
|
||||||
|
private final OrderService orderService;
|
||||||
|
public OrderController(OrderService orderService) {
|
||||||
|
this.orderService = orderService;
|
||||||
|
}
|
||||||
|
@GetMapping("/coffeesByOrder")
|
||||||
|
public List<Pair<CoffeeDto, Integer>> getCoffeesByOrder(
|
||||||
|
@RequestParam(value = "orderId") Long orderId
|
||||||
|
){
|
||||||
|
return orderService.findCoffeesByOrder(orderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/byDate")
|
||||||
|
public List<OrderCoffees> getOrdersByDate(
|
||||||
|
@RequestParam(value = "startDate") String startDate,
|
||||||
|
@RequestParam(value = "endDate") String endDate
|
||||||
|
) throws ParseException {
|
||||||
|
return orderService.findOrdersByDate(startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/byUser")
|
||||||
|
public List<OrderDto> getOrdersByUser(
|
||||||
|
@RequestParam(value = "userId") Long userId,
|
||||||
|
@RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo,
|
||||||
|
@RequestParam(value = "pageSize", defaultValue = "10", required = false) int pageSize,
|
||||||
|
@RequestParam(value = "sortBy", defaultValue = "id", required = false) String sortBy,
|
||||||
|
@RequestParam(value = "sortDir", defaultValue = "asc", required = false) String sortDir
|
||||||
|
) {
|
||||||
|
return orderService.findOrderByUser(userId, pageNo, pageSize, sortBy, sortDir).stream()
|
||||||
|
.map(OrderDto::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/coffeeCrossRef")
|
||||||
|
public List<OrderCoffeeCrossRef> getOrdersByDate() {
|
||||||
|
return orderService.findOrderCoffeeCrossRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/")
|
||||||
|
public OrderDto createOrder(@RequestBody @Valid OrderDto orderDto) throws ParseException {
|
||||||
|
return new OrderDto(orderService.addOrder(orderDto.getDate(), orderDto.getSum(), orderDto.getUser().getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/coffeeCrossRef")
|
||||||
|
public void createOrderCoffeeCrossRef(@RequestBody @Valid OrderCoffeeCrossRefDto orderCoffeeCrossRefDto) {
|
||||||
|
orderService.createOrderCoffeeCrossRef(orderCoffeeCrossRefDto.getOrderId(), orderCoffeeCrossRefDto.getCoffeeId(), orderCoffeeCrossRefDto.getCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/coffeeCrossRef/{id}")
|
||||||
|
public void deleteOrderCoffeeCrossRef(@PathVariable Long id) {
|
||||||
|
orderService.deleteOrderCoffeeCrossRef(id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.kalyshev.yan.order.controller;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.order.model.Order;
|
||||||
|
import com.kalyshev.yan.user.model.User;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public class OrderDto {
|
||||||
|
private Long id;
|
||||||
|
private String date;
|
||||||
|
private Double sum;
|
||||||
|
private User user;
|
||||||
|
public OrderDto(){}
|
||||||
|
public OrderDto(Order order) {
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
|
||||||
|
this.id = order.getId();
|
||||||
|
this.date = format.format(order.getDate());
|
||||||
|
this.sum = order.getSum();
|
||||||
|
this.user = order.getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public String getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
public void setDate(String date) {
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
public Double getSum() {
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
public void setSum(Double sum) {
|
||||||
|
this.sum = sum;
|
||||||
|
}
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.kalyshev.yan.order.model;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.user.model.User;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "order_o")
|
||||||
|
public class Order {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
@Temporal(TemporalType.DATE)
|
||||||
|
private Date date;
|
||||||
|
private Double sum;
|
||||||
|
@ManyToOne(cascade = {CascadeType.MERGE})
|
||||||
|
@JoinColumn(name = "user_id", unique = false)
|
||||||
|
private User user;
|
||||||
|
public Order(Date date, Double sum, User user) {
|
||||||
|
this.date = date;
|
||||||
|
this.sum = sum;
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
public Order(Long id, Date date, Double sum, User user) {
|
||||||
|
this.id = id;
|
||||||
|
this.date = date;
|
||||||
|
this.sum = sum;
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
public Order() {}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
public void setDate(Date date) {
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
public Double getSum() {
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
public void setSum(Double sum) {
|
||||||
|
this.sum = sum;
|
||||||
|
}
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.kalyshev.yan.order.model;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "order_coffee")
|
||||||
|
public class OrderCoffeeCrossRef {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
@ManyToOne(cascade = {CascadeType.MERGE})
|
||||||
|
@JoinColumn(name = "order_id", unique = false)
|
||||||
|
private Order order;
|
||||||
|
@ManyToOne(cascade = {CascadeType.MERGE})
|
||||||
|
@JoinColumn(name = "coffee_id", unique = false)
|
||||||
|
private Coffee coffee;
|
||||||
|
@Column(name = "count")
|
||||||
|
private int count;
|
||||||
|
public OrderCoffeeCrossRef(Order order, Coffee coffee, int count) {
|
||||||
|
this.order = order;
|
||||||
|
this.coffee = coffee;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
public OrderCoffeeCrossRef(Long id, Order order, Coffee coffee, int count) {
|
||||||
|
this.id = id;
|
||||||
|
this.order = order;
|
||||||
|
this.coffee = coffee;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
public OrderCoffeeCrossRef() {}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public Order getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
public void setOrder(Order order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
public Coffee getCoffee() {
|
||||||
|
return coffee;
|
||||||
|
}
|
||||||
|
public void setCoffee(Coffee coffee) {
|
||||||
|
this.coffee = coffee;
|
||||||
|
}
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
public void setCount(int count) {
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.kalyshev.yan.order.model;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.controller.CoffeeDto;
|
||||||
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
import com.kalyshev.yan.order.controller.OrderDto;
|
||||||
|
import org.springframework.data.util.Pair;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class OrderCoffees {
|
||||||
|
private OrderDto order;
|
||||||
|
private List<Pair<CoffeeDto, Integer>> coffees;
|
||||||
|
public OrderCoffees() {
|
||||||
|
|
||||||
|
}
|
||||||
|
public OrderCoffees(OrderDto order, List<Pair<CoffeeDto, Integer>> coffees) {
|
||||||
|
this.order = order;
|
||||||
|
this.coffees = coffees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderDto getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
public void setOrder(OrderDto order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
public List<Pair<CoffeeDto, Integer>> getCoffees() {
|
||||||
|
return coffees;
|
||||||
|
}
|
||||||
|
public void setCoffees(List<Pair<CoffeeDto, Integer>> coffees) {
|
||||||
|
this.coffees = coffees;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.kalyshev.yan.order.repository;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffeeCrossRef;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface OrderCoffeeCrossRefRepository extends JpaRepository<OrderCoffeeCrossRef, Long> {
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.kalyshev.yan.order.repository;
|
||||||
|
|
||||||
|
public class OrderNotFoundException extends RuntimeException {
|
||||||
|
public OrderNotFoundException(Long id) {
|
||||||
|
super(String.format("Order with id [%s] is not found", id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.kalyshev.yan.order.repository;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
import com.kalyshev.yan.order.model.Order;
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffeeCrossRef;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface OrderRepository extends JpaRepository<Order, Long> {
|
||||||
|
|
||||||
|
@Query(value = """
|
||||||
|
select * from order_o
|
||||||
|
where order_o.date >= :start_date and order_o.date <= :end_date
|
||||||
|
""", nativeQuery = true)
|
||||||
|
public List<Order> findOrdersByDate(@Param("start_date") Date startDate,
|
||||||
|
@Param("end_date") Date endDate);
|
||||||
|
|
||||||
|
@Query(value = """
|
||||||
|
select new OrderCoffeeCrossRef(ord.id, ord.order, ord.coffee, ord.count) from OrderCoffeeCrossRef ord
|
||||||
|
""")
|
||||||
|
public List<OrderCoffeeCrossRef> findOrderCoffeeCrossRef();
|
||||||
|
|
||||||
|
public Page<Order> findOrdersByUser(Long userId, Pageable pageable);
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package com.kalyshev.yan.order.service;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.controller.CoffeeDto;
|
||||||
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
import com.kalyshev.yan.coffee.repository.CoffeeNotFoundException;
|
||||||
|
import com.kalyshev.yan.coffee.repository.CoffeeRepository;
|
||||||
|
import com.kalyshev.yan.order.controller.OrderCoffeeCrossRefDto;
|
||||||
|
import com.kalyshev.yan.order.controller.OrderDto;
|
||||||
|
import com.kalyshev.yan.order.model.Order;
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffeeCrossRef;
|
||||||
|
import com.kalyshev.yan.order.model.OrderCoffees;
|
||||||
|
import com.kalyshev.yan.order.repository.OrderCoffeeCrossRefRepository;
|
||||||
|
import com.kalyshev.yan.order.repository.OrderNotFoundException;
|
||||||
|
import com.kalyshev.yan.order.repository.OrderRepository;
|
||||||
|
import com.kalyshev.yan.user.model.User;
|
||||||
|
import com.kalyshev.yan.user.repository.UserNotFoundException;
|
||||||
|
import com.kalyshev.yan.user.repository.UserRepository;
|
||||||
|
import com.kalyshev.yan.util.validation.ValidatorUtil;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.util.Pair;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class OrderService {
|
||||||
|
private final OrderRepository orderRepository;
|
||||||
|
private final OrderCoffeeCrossRefRepository orderCoffeeCrossRefRepository;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final CoffeeRepository coffeeRepository;
|
||||||
|
private final ValidatorUtil validatorUtil;
|
||||||
|
public OrderService(OrderRepository orderRepository,
|
||||||
|
OrderCoffeeCrossRefRepository orderCoffeeCrossRefRepository,
|
||||||
|
UserRepository userRepository,
|
||||||
|
CoffeeRepository coffeeRepository,
|
||||||
|
ValidatorUtil validatorUtil) {
|
||||||
|
this.orderRepository = orderRepository;
|
||||||
|
this.orderCoffeeCrossRefRepository = orderCoffeeCrossRefRepository;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.coffeeRepository = coffeeRepository;
|
||||||
|
this.validatorUtil = validatorUtil;
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public Order addOrder(String date, Double sum, Long userId) throws ParseException {
|
||||||
|
if (!StringUtils.hasText(date)) {
|
||||||
|
throw new IllegalArgumentException("Date is null or empty");
|
||||||
|
}
|
||||||
|
if (sum <= 0) {
|
||||||
|
throw new IllegalArgumentException("Sum is null or negative");
|
||||||
|
}
|
||||||
|
if (userId <= 0) {
|
||||||
|
throw new IllegalArgumentException("UserId is null or negative");
|
||||||
|
}
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
|
||||||
|
final User user = userRepository.findById(userId).orElseThrow(() -> new UserNotFoundException(userId));
|
||||||
|
final Order order = new Order(format.parse(date), sum, user);
|
||||||
|
validatorUtil.validate(order);
|
||||||
|
return orderRepository.save(order);
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<Pair<CoffeeDto, Integer>> findCoffeesByOrder(Long orderId) {
|
||||||
|
List<Object[]> coffees = coffeeRepository.findCoffeesByOrder(orderId);
|
||||||
|
List<Pair<CoffeeDto, Integer>> resultCoffees = new ArrayList<>();
|
||||||
|
for (Object[] coffee : coffees) {
|
||||||
|
resultCoffees.add(Pair.of(new CoffeeDto((Coffee) coffee[0]),(Integer) coffee[1]));
|
||||||
|
}
|
||||||
|
return resultCoffees;
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<OrderCoffees> findOrdersByDate(String startDate, String endDate) throws ParseException {
|
||||||
|
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", Locale.ROOT);
|
||||||
|
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
|
||||||
|
Date startDateDate = formatter.parse(startDate);
|
||||||
|
Date endDateDate = formatter.parse(endDate);
|
||||||
|
List<Order> orders = orderRepository.findOrdersByDate(startDateDate, endDateDate);
|
||||||
|
List<OrderCoffees> orderCoffees = new ArrayList<OrderCoffees>();
|
||||||
|
for (Order order : orders) {
|
||||||
|
List<Object[]> coffees = coffeeRepository.findCoffeesByOrder(order.getId());
|
||||||
|
List<Pair<CoffeeDto, Integer>> resultCoffees = new ArrayList<>();
|
||||||
|
for (Object[] coffee : coffees) {
|
||||||
|
resultCoffees.add(Pair.of(new CoffeeDto((Coffee) coffee[0]),(Integer) coffee[1]));
|
||||||
|
}
|
||||||
|
orderCoffees.add(new OrderCoffees(new OrderDto(order), resultCoffees));
|
||||||
|
}
|
||||||
|
return orderCoffees;
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<OrderCoffeeCrossRef> findOrderCoffeeCrossRef() {
|
||||||
|
List<OrderCoffeeCrossRef> orderCoffeeCrossRefs = orderRepository.findOrderCoffeeCrossRef();
|
||||||
|
return orderCoffeeCrossRefs;
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public void createOrderCoffeeCrossRef(Long orderId, Long coffeeId, int count) {
|
||||||
|
final Order order = orderRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException(orderId));
|
||||||
|
final Coffee coffee = coffeeRepository.findById(coffeeId).orElseThrow(() -> new CoffeeNotFoundException(coffeeId));
|
||||||
|
final OrderCoffeeCrossRef orderCoffeeCrossRef = new OrderCoffeeCrossRef(order, coffee, count);
|
||||||
|
validatorUtil.validate(orderCoffeeCrossRef);
|
||||||
|
orderCoffeeCrossRefRepository.save(orderCoffeeCrossRef);
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<Order> findOrderByUser(Long userId, int pageNo, int pageSize, String sortBy, String sortDir) {
|
||||||
|
Sort sort = sortDir.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortBy).ascending()
|
||||||
|
: Sort.by(sortBy).descending();
|
||||||
|
|
||||||
|
// create Pageable instance
|
||||||
|
Pageable pageable = PageRequest.of(pageNo, pageSize, sort);
|
||||||
|
|
||||||
|
Page<Order> orders = orderRepository.findOrdersByUser(userId, pageable);
|
||||||
|
|
||||||
|
// get content for page object
|
||||||
|
List<Order> listOfOrders = orders.getContent();
|
||||||
|
|
||||||
|
return listOfOrders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteOrderCoffeeCrossRef(Long id) {
|
||||||
|
orderCoffeeCrossRefRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ import org.springframework.data.repository.query.Param;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface UserRepository extends JpaRepository<User, Long> {
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
@Query(value = "select * from \"USER_U\" where \"LOGIN\" = :login and \"PASSWORD\" = :password", nativeQuery = true)
|
@Query(value = "select * from user_u where login = :login and password = :password", nativeQuery = true)
|
||||||
public Optional<User> tryLogin(@Param("login") String login,
|
public Optional<User> tryLogin(@Param("login") String login,
|
||||||
@Param("password") String password);
|
@Param("password") String password);
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.kalyshev.yan.util.error;
|
package com.kalyshev.yan.util.error;
|
||||||
|
|
||||||
import com.kalyshev.yan.coffee.repository.CoffeeNotFoundException;
|
import com.kalyshev.yan.coffee.repository.CoffeeNotFoundException;
|
||||||
|
import com.kalyshev.yan.order.repository.OrderNotFoundException;
|
||||||
import com.kalyshev.yan.user.repository.UserNotFoundException;
|
import com.kalyshev.yan.user.repository.UserNotFoundException;
|
||||||
import com.kalyshev.yan.util.validation.ValidationException;
|
import com.kalyshev.yan.util.validation.ValidationException;
|
||||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||||
@ -18,7 +19,8 @@ public class AdviceController {
|
|||||||
@ExceptionHandler({
|
@ExceptionHandler({
|
||||||
CoffeeNotFoundException.class,
|
CoffeeNotFoundException.class,
|
||||||
UserNotFoundException.class,
|
UserNotFoundException.class,
|
||||||
ValidationException.class
|
ValidationException.class,
|
||||||
|
OrderNotFoundException.class
|
||||||
})
|
})
|
||||||
public ResponseEntity<Object> handleException(Throwable e) {
|
public ResponseEntity<Object> handleException(Throwable e) {
|
||||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
spring.main.banner-mode=off
|
|
||||||
#server.port=8080
|
|
||||||
spring.datasource.url=jdbc:h2:file:./data
|
|
||||||
spring.datasource.driverClassName=org.h2.Driver
|
|
||||||
spring.datasource.username=sa
|
|
||||||
spring.datasource.password=password
|
|
||||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
|
||||||
spring.jpa.hibernate.ddl-auto=update
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
spring.h2.console.enabled=true
|
spring.sql.init.mode=always
|
||||||
spring.h2.console.settings.trace=false
|
spring.sql.init.platform=postgres
|
||||||
spring.h2.console.settings.web-allow-others=true
|
spring.datasource.url=jdbc:postgresql://109.197.199.134:5432/coffee
|
||||||
|
spring.datasource.username=postgres
|
||||||
|
spring.datasource.password=gAiCyfGGv5TywE
|
||||||
|
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
|
||||||
|
Loading…
Reference in New Issue
Block a user