some done
This commit is contained in:
parent
f79fd6d658
commit
105378ca52
@ -2,6 +2,7 @@ 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.OrderRemote
|
||||||
import com.zyzf.coffeepreorder.api.model.UserRemote
|
import com.zyzf.coffeepreorder.api.model.UserRemote
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
@ -77,8 +78,32 @@ interface MyServerService {
|
|||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
): CoffeeRemote
|
): CoffeeRemote
|
||||||
|
|
||||||
|
@GET("/orders/byDate")
|
||||||
|
suspend fun getOrdersByDate(
|
||||||
|
@Query("startDate") startDate: String,
|
||||||
|
@Query("endDate") endDate: String,
|
||||||
|
): List<OrderRemote>
|
||||||
|
|
||||||
|
@POST("orders")
|
||||||
|
suspend fun createOrder(
|
||||||
|
@Body order: OrderRemote,
|
||||||
|
): OrderRemote
|
||||||
|
|
||||||
|
@GET("/orders/byUser")
|
||||||
|
suspend fun getOrdersByUser(
|
||||||
|
@Query("userId") userId: Int,
|
||||||
|
@Query("pageNo") page: Int,
|
||||||
|
@Query("pageSize") limit: Int,
|
||||||
|
): List<OrderRemote>
|
||||||
|
|
||||||
|
@GET("/orders/coffeesByOrder")
|
||||||
|
suspend fun getCoffeesByOrder(
|
||||||
|
@Query("orderId") orderId: Int,
|
||||||
|
): List<CoffeeRemote>
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val BASE_URL = "http://192.168.0.100:8080/api/"
|
private const val BASE_URL = "http://192.168.42.59:8080/api/"
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var INSTANCE: MyServerService? = null
|
private var INSTANCE: MyServerService? = null
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderCoffeeCrossRefRemote.toOrderCoffeeCrossRef(): OrderCoffeeCrossRef = OrderCoffeeCrossRef(
|
||||||
|
orderId,
|
||||||
|
coffeeId
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderCoffeeCrossRef.toOrderCoffeeCrossRefRemote(): OrderCoffeeCrossRefRemote = OrderCoffeeCrossRefRemote(
|
||||||
|
orderId,
|
||||||
|
coffeeId
|
||||||
|
)
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.model
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.OrderWithCoffees
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OrderCoffeeRemote(
|
||||||
|
val order: OrderRemote = OrderRemote(),
|
||||||
|
val coffees: List<CoffeeRemote> = listOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderCoffeeRemote.toOrdersWithCoffees(): OrderWithCoffees {
|
||||||
|
val convertedOrder = this.order.toOrder()
|
||||||
|
val convertedCoffees = this.coffees.map { it.toCoffee() }
|
||||||
|
return OrderWithCoffees(convertedOrder, convertedCoffees)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OrderWithCoffees.toOrderCoffeeRemote(): OrderCoffeeRemote {
|
||||||
|
val convertedOrder = this.order.toOrderRemote()
|
||||||
|
val convertedCoffees = this.coffees.map { it.toCoffeeRemote() }
|
||||||
|
return OrderCoffeeRemote(convertedOrder, convertedCoffees)
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.model
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Order
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OrderRemote(
|
||||||
|
val id: Int = 0,
|
||||||
|
val date: String = "",
|
||||||
|
val userId: Int = 0,
|
||||||
|
val sum: Double = 0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OrderRemote.toOrder(): Order = Order(
|
||||||
|
id,
|
||||||
|
date,
|
||||||
|
userId,
|
||||||
|
sum
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Order.toOrderRemote(): OrderRemote = OrderRemote(
|
||||||
|
uid,
|
||||||
|
date,
|
||||||
|
userId,
|
||||||
|
sum
|
||||||
|
)
|
@ -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!!.uid, 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.uid,
|
||||||
|
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.uid, 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.uid, RemoteKeyType.ORDER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyClosestToCurrentPosition(
|
||||||
|
state: PagingState<Int, Order>
|
||||||
|
): RemoteKeys? {
|
||||||
|
return state.anchorPosition?.let { position ->
|
||||||
|
state.closestItemToPosition(position)?.uid?.let { orderUid ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(orderUid, RemoteKeyType.ORDER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
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.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.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<Order>> {
|
||||||
|
return flowOf(service.getOrdersByDate(startDate, endDate).map { it.toOrder() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
service.createOrder(order.toOrderRemote()).toOrder()
|
||||||
|
}
|
||||||
|
}
|
@ -44,18 +44,18 @@ 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().uid?.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.uid, user.toUserRemote()).toUser().uid
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(user: User) {
|
override suspend fun delete(user: User) {
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
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 kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface OrderDao {
|
||||||
|
@Query("select * from order by name collate nocase asc")
|
||||||
|
fun getAll(): PagingSource<Int, Order>
|
||||||
|
|
||||||
|
@Query("select * from order where order.uid = :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 order.uid = ordercoffeecrossref.coffee_id
|
||||||
|
WHERE ordercoffeecrossref.order_id = :orderId
|
||||||
|
""")
|
||||||
|
fun getCoffeesByOrder(orderId: Int): Flow<List<Coffee>>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT order.* FROM order
|
||||||
|
WHERE date >= :startDate and date <= :endDate
|
||||||
|
""")
|
||||||
|
fun getOrdersByDate(startDate: String, endDate: String): Flow<List<Order>>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(order: Order)
|
||||||
|
|
||||||
|
@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 order: OrderCoffeeCrossRef)
|
||||||
|
@Delete
|
||||||
|
suspend fun delete(order: OrderCoffeeCrossRef)
|
||||||
|
}
|
@ -20,7 +20,7 @@ data class Cart(
|
|||||||
val uid: Int = 0,
|
val uid: 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
|
||||||
|
@ -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.Index
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "order",
|
||||||
|
foreignKeys = [ForeignKey(
|
||||||
|
entity = User::class,
|
||||||
|
parentColumns = arrayOf("uid"),
|
||||||
|
childColumns = arrayOf("user_id"),
|
||||||
|
onDelete = ForeignKey.RESTRICT,
|
||||||
|
onUpdate = ForeignKey.RESTRICT
|
||||||
|
)],
|
||||||
|
indices = [Index(value = ["user_id"])]
|
||||||
|
)
|
||||||
|
data class Order(
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
val uid: 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
|
||||||
|
) : this(0, date, userId, 0.0)
|
||||||
|
|
||||||
|
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 (uid != other.uid) return false
|
||||||
|
if (date != other.date) return false
|
||||||
|
if (userId != other.userId) return false
|
||||||
|
return sum == other.sum
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = uid
|
||||||
|
result = 31 * result + date.hashCode()
|
||||||
|
result = 31 * result + userId.hashCode()
|
||||||
|
result = 31 * result + sum.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
|
)
|
@ -0,0 +1,15 @@
|
|||||||
|
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 = "coffee_id",
|
||||||
|
associateBy = Junction(OrderCoffeeCrossRef::class)
|
||||||
|
)
|
||||||
|
val coffees: List<Coffee>
|
||||||
|
)
|
@ -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 }
|
||||||
|
@ -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.dao.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<Order>> = 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,13 @@
|
|||||||
|
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 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)
|
||||||
|
suspend fun getOrdersByDate(startDate: String, endDate: String): Flow<List<Order>>
|
||||||
|
}
|
@ -25,6 +25,6 @@ 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)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,213 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.statistic
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.PickVisualMediaRequest
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
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.Spacer
|
||||||
|
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.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material.icons.outlined.Create
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.OutlinedIconButton
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SheetState
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableDoubleStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.compose.LazyPagingItems
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import androidx.paging.compose.itemContentType
|
||||||
|
import androidx.paging.compose.itemKey
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
|
import com.zyzf.coffeepreorder.R
|
||||||
|
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.ui.AppViewModelProvider
|
||||||
|
import com.zyzf.coffeepreorder.ui.coffee.CoffeeListViewModel
|
||||||
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
|
import eu.bambooapps.material3.pullrefresh.PullRefreshIndicator
|
||||||
|
import eu.bambooapps.material3.pullrefresh.pullRefresh
|
||||||
|
import eu.bambooapps.material3.pullrefresh.rememberPullRefreshState
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun OrderStatistic(
|
||||||
|
viewModel: CoffeeListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val orderListUiState = viewModel.coffeeListUiState.collectAsLazyPagingItems()
|
||||||
|
val openDialog = remember { mutableStateOf(false) }
|
||||||
|
var refreshing by remember { mutableStateOf(false) }
|
||||||
|
fun refresh() = coroutineScope.launch {
|
||||||
|
refreshing = true
|
||||||
|
orderListUiState.refresh()
|
||||||
|
refreshing = false
|
||||||
|
}
|
||||||
|
val state = rememberPullRefreshState(refreshing, ::refresh)
|
||||||
|
Scaffold(
|
||||||
|
topBar = {},
|
||||||
|
floatingActionButton = {
|
||||||
|
if (CoffeeApplication.currentUser?.role == "admin") {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Modifier
|
||||||
|
.padding(all = 20.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Check,
|
||||||
|
contentDescription = "Add",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Box (modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.pullRefresh(state)) {
|
||||||
|
PullRefreshIndicator(refreshing = refreshing, state = state,
|
||||||
|
modifier = Modifier
|
||||||
|
.zIndex(100f)
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
|
)
|
||||||
|
OrderList(
|
||||||
|
orderList = orderListUiState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OrderList(
|
||||||
|
orderList: LazyPagingItems<OrderWithCoffees>,
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
items(
|
||||||
|
count = orderList.itemCount,
|
||||||
|
key = orderList.itemKey(),
|
||||||
|
contentType = orderList.itemContentType()
|
||||||
|
) {index ->
|
||||||
|
val order = orderList[index]
|
||||||
|
order?.let {
|
||||||
|
OrderListItem(
|
||||||
|
order = order
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OrderListItem(
|
||||||
|
order: OrderWithCoffees
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(max = 140.dp)
|
||||||
|
.padding(bottom = 10.dp, top = 10.dp)) {
|
||||||
|
Text(text = order.order.userId.toString(), fontSize = 25.sp)
|
||||||
|
Text(text = String.format("%.2f", order.coffees.sumOf { it.cost }), fontSize = 20.sp)
|
||||||
|
order.coffees.map {
|
||||||
|
Text(text = it.name, fontSize = 15.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//@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 CoffeeListPreview() {
|
||||||
|
// CoffeePreorderTheme {
|
||||||
|
// Surface(
|
||||||
|
// color = MaterialTheme.colorScheme.background
|
||||||
|
// ) {
|
||||||
|
// OrderList(
|
||||||
|
// orderList = MutableStateFlow(
|
||||||
|
// PagingData.from((1..20).map { i -> Coffee.getCoffee(i) })
|
||||||
|
// ).collectAsLazyPagingItems(),
|
||||||
|
// onAddToCartClick = {}
|
||||||
|
// ) {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
//@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 CoffeeEmptyListPreview() {
|
||||||
|
// CoffeePreorderTheme {
|
||||||
|
// Surface(
|
||||||
|
// color = MaterialTheme.colorScheme.background
|
||||||
|
// ) {
|
||||||
|
// CoffeeList(
|
||||||
|
// coffeeList = MutableStateFlow(
|
||||||
|
// PagingData.empty<Coffee>()
|
||||||
|
// ).collectAsLazyPagingItems(),
|
||||||
|
// onAddToCartClick = {}
|
||||||
|
// ) {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.statistic
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.ui.coffee.copyFileToSftp
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
|
class OrderStatisticViewModel(
|
||||||
|
private val coffeeRepository: CoffeeRepository,
|
||||||
|
private val cartRepository: CartRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
val coffeeListUiState: Flow<PagingData<Coffee>> = coffeeRepository.getAllCoffees()
|
||||||
|
|
||||||
|
suspend fun addCoffeeToCart(coffeeUid: Int) {
|
||||||
|
cartRepository.insertCoffee(coffeeUid, 1)
|
||||||
|
}
|
||||||
|
suspend fun createCoffee(coffee: Coffee, imageUri: Uri, context: Context) {
|
||||||
|
val newCoffee: Long = coffeeRepository.insert(coffee)
|
||||||
|
val inputStream = context.contentResolver.openInputStream(imageUri)
|
||||||
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
|
||||||
|
val f = File(context.cacheDir, "coffee_image_$newCoffee.png")
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
f.createNewFile()
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
|
||||||
|
val bitmapdata = bos.toByteArray()
|
||||||
|
val fos = FileOutputStream(f)
|
||||||
|
fos.write(bitmapdata)
|
||||||
|
fos.flush()
|
||||||
|
fos.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
|
||||||
|
}
|
||||||
|
suspend fun editCoffee(coffee: Coffee, imageUri: Any?, context: Context) {
|
||||||
|
val editedCoffee: Int = coffeeRepository.update(coffee)
|
||||||
|
if (imageUri !is Uri) return
|
||||||
|
val inputStream = context.contentResolver.openInputStream(imageUri)
|
||||||
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
|
||||||
|
val f = File(context.cacheDir, "coffee_image_$editedCoffee.png")
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
f.createNewFile()
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
|
||||||
|
val bitmapdata = bos.toByteArray()
|
||||||
|
val fos = FileOutputStream(f)
|
||||||
|
fos.write(bitmapdata)
|
||||||
|
fos.flush()
|
||||||
|
fos.close()
|
||||||
|
}
|
||||||
|
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
|
||||||
|
}
|
||||||
|
suspend fun deleteCoffee(coffee: Coffee) {
|
||||||
|
coffeeRepository.delete(coffee)
|
||||||
|
}
|
||||||
|
}
|
@ -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.42.59</domain>
|
||||||
</domain-config>
|
</domain-config>
|
||||||
</network-security-config>
|
</network-security-config>
|
Loading…
x
Reference in New Issue
Block a user