First big update :)

This commit is contained in:
ElEgEv
2023-12-22 22:24:57 +04:00
parent 2fdc2d5ba3
commit 2b69f0fc24
13 changed files with 157 additions and 55 deletions

Binary file not shown.

View File

@@ -109,6 +109,11 @@ interface ServerService {
@GET(ApiRoutes.TANK) @GET(ApiRoutes.TANK)
suspend fun getTanks(): List<TankRemote> suspend fun getTanks(): List<TankRemote>
@GET(ApiRoutes.TANK)
suspend fun getTanks(
@Query("id") id: List<Long>
): List<TankRemote>
@GET("${ApiRoutes.TANK}/{id}") @GET("${ApiRoutes.TANK}/{id}")
suspend fun getTank( suspend fun getTank(
@Path("id") id: Long @Path("id") id: Long
@@ -144,8 +149,10 @@ interface ServerService {
// :[USER_TANK_CROSS_REF] // :[USER_TANK_CROSS_REF]
@GET(ApiRoutes.USER_TANK) @GET("${ApiRoutes.USER_TANK}")
suspend fun getUserTankCrossRef(): List<UserTankCrossRefRemote> suspend fun getUserTankCrossRef(
@Query("id") id: Long
): List<UserTankCrossRefRemote>
@POST(ApiRoutes.USER_TANK) @POST(ApiRoutes.USER_TANK)
suspend fun insertUserTankCrossRef( suspend fun insertUserTankCrossRef(

View File

@@ -7,13 +7,11 @@ import androidx.paging.RemoteMediator
import androidx.room.withTransaction import androidx.room.withTransaction
import retrofit2.HttpException import retrofit2.HttpException
import ru.ulstu.`is`.pmu.tank.api.ServerService import ru.ulstu.`is`.pmu.tank.api.ServerService
import ru.ulstu.`is`.pmu.tank.api.model.toNation
import ru.ulstu.`is`.pmu.tank.api.model.toTank import ru.ulstu.`is`.pmu.tank.api.model.toTank
import ru.ulstu.`is`.pmu.tank.database.AppDatabase import ru.ulstu.`is`.pmu.tank.database.AppDatabase
import ru.ulstu.`is`.pmu.tank.model.Nation
import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType
import ru.ulstu.`is`.pmu.tank.model.RemoteKeys import ru.ulstu.`is`.pmu.tank.model.RemoteKeys
import ru.ulstu.`is`.pmu.tank.repository.OfflineNationRepository import ru.ulstu.`is`.pmu.tank.model.Tank
import ru.ulstu.`is`.pmu.tank.repository.OfflineRemoteKeyRepository import ru.ulstu.`is`.pmu.tank.repository.OfflineRemoteKeyRepository
import ru.ulstu.`is`.pmu.tank.repository.OfflineTankRepository import ru.ulstu.`is`.pmu.tank.repository.OfflineTankRepository
import java.io.IOException import java.io.IOException
@@ -24,7 +22,7 @@ class TankRemoteMediator(
private val dbTankRepository: OfflineTankRepository, private val dbTankRepository: OfflineTankRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: AppDatabase private val database: AppDatabase
) : RemoteMediator<Int, Nation>() { ) : RemoteMediator<Int, Tank>() {
override suspend fun initialize(): InitializeAction { override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH return InitializeAction.LAUNCH_INITIAL_REFRESH
@@ -32,7 +30,7 @@ class TankRemoteMediator(
override suspend fun load( override suspend fun load(
loadType: LoadType, loadType: LoadType,
state: PagingState<Int, Nation> state: PagingState<Int, Tank>
): MediatorResult { ): MediatorResult {
val page = when (loadType) { val page = when (loadType) {
LoadType.REFRESH -> { LoadType.REFRESH -> {
@@ -85,26 +83,26 @@ class TankRemoteMediator(
} }
} }
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Nation>): RemoteKeys? { private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Tank>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull() return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { nation -> ?.let { tank ->
dbRemoteKeyRepository.getAllRemoteKeys(nation.uid!!.toInt(), RemoteKeyType.NATION) dbRemoteKeyRepository.getAllRemoteKeys(tank.tankId!!.toInt(), RemoteKeyType.NATION)
} }
} }
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Nation>): RemoteKeys? { private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Tank>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull() return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { nation -> ?.let { tank ->
dbRemoteKeyRepository.getAllRemoteKeys(nation.uid!!.toInt(), RemoteKeyType.NATION) dbRemoteKeyRepository.getAllRemoteKeys(tank.tankId!!.toInt(), RemoteKeyType.NATION)
} }
} }
private suspend fun getRemoteKeyClosestToCurrentPosition( private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Nation> state: PagingState<Int, Tank>
): RemoteKeys? { ): RemoteKeys? {
return state.anchorPosition?.let { position -> return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.uid?.let { nationUid -> state.closestItemToPosition(position)?.tankId?.let { tankUid ->
dbRemoteKeyRepository.getAllRemoteKeys(nationUid.toInt(), RemoteKeyType.NATION) dbRemoteKeyRepository.getAllRemoteKeys(tankUid.toInt(), RemoteKeyType.NATION)
} }
} }
} }

View File

@@ -1,21 +1,27 @@
package ru.ulstu.`is`.pmu.tank.api.model package ru.ulstu.`is`.pmu.tank.api.model
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import ru.ulstu.`is`.pmu.tank.database.DateSerializer
import ru.ulstu.`is`.pmu.tank.model.Level import ru.ulstu.`is`.pmu.tank.model.Level
import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef
import java.util.Date
@Serializable @Serializable
data class UserTankCrossRefRemote ( data class UserTankCrossRefRemote (
val id: Long = 0, val id: Long = 0,
val tankId: Long = 0 val tankId: Long = 0,
@Serializable(with = DateSerializer::class)
val date: Date = Date()
) )
fun UserTankCrossRefRemote.toUserTankCrossRef(): UserTankCrossRef = UserTankCrossRef( fun UserTankCrossRefRemote.toUserTankCrossRef(): UserTankCrossRef = UserTankCrossRef(
userId = id, userId = id,
tankId = tankId tankId = tankId,
date = date
) )
fun UserTankCrossRef.toRemote(): UserTankCrossRefRemote = UserTankCrossRefRemote( fun UserTankCrossRef.toRemote(): UserTankCrossRefRemote = UserTankCrossRefRemote(
id = userId, id = userId,
tankId = tankId tankId = tankId,
date = date
) )

View File

@@ -26,6 +26,7 @@ import ru.ulstu.`is`.pmu.tank.repository.OfflineUsersTanksRepository
import ru.ulstu.`is`.pmu.tank.repository.TankRepository import ru.ulstu.`is`.pmu.tank.repository.TankRepository
import ru.ulstu.`is`.pmu.tank.repository.UsersTanksRepository import ru.ulstu.`is`.pmu.tank.repository.UsersTanksRepository
import java.nio.file.Files.find import java.nio.file.Files.find
import java.util.Date
class RestTankRepository ( class RestTankRepository (
private val service: ServerService, private val service: ServerService,
@@ -38,13 +39,13 @@ class RestTankRepository (
override suspend fun getForUserAll(userId: Long): List<Tank> { override suspend fun getForUserAll(userId: Long): List<Tank> {
val totalList: List<Tank> = getAll() val totalList: List<Tank> = getAll()
val totalUserTankList: List<UserTankCrossRef> = service.getUserTankCrossRef().map{ it.toUserTankCrossRef() } val totalUserTankList: List<UserTankCrossRef> = service.getUserTankCrossRef(100L).map{ it.toUserTankCrossRef() }
//спискок, который вернём //спискок, который вернём
var supportList = ArrayList<Tank>() var supportList = ArrayList<Tank>()
//вспомогательный список для проверки отсутствия танка у пользователя //вспомогательный список для проверки отсутствия танка у пользователя
var notToUser: List<UserTankCrossRef> = totalList.map{ UserTankCrossRef(userId, it.tankId!!) } var notToUser: List<UserTankCrossRef> = totalList.map{ UserTankCrossRef(userId, it.tankId!!, Date()) }
if (totalUserTankList.isEmpty()){ if (totalUserTankList.isEmpty()){
return totalList return totalList
@@ -72,26 +73,31 @@ class RestTankRepository (
val totalNationList: List<Nation> = service.getAllNations().map { it.toNation() } val totalNationList: List<Nation> = service.getAllNations().map { it.toNation() }
//все имеющиеся танки у пользователя //все имеющиеся танки у пользователя
val totalUserTankList: List<UserTankCrossRef> = service.getUserTankCrossRef().map{ it.toUserTankCrossRef() } val totalUserTankList: List<UserTankCrossRef> = service.getUserTankCrossRef(100L).map{ it.toUserTankCrossRef() }
//спискок, который вернём //спискок, который вернём
var supportList = ArrayList<TankWithNationAndLevel>() var supportList = ArrayList<TankWithNationAndLevel>()
//вспомогательный список для проверки наличия танка у пользователя //список id-шников танков пользователя
var notToUser: List<UserTankCrossRef> = totalList.map{ UserTankCrossRef(userId, it.tankId!!) } var idList = ArrayList<Long>()
totalUserTankList.forEach(){ tank ->
idList.add(tank.tankId)
}
//список танков пользователя
var simpleTankList = service.getTanks(idList)
if (totalUserTankList.isEmpty()){ if (totalUserTankList.isEmpty()){
return listOf() return listOf()
} else { } else {
notToUser.forEach(){userTank -> simpleTankList.forEach(){userTank ->
if(totalUserTankList.contains(userTank)){ val tank = totalList.firstOrNull { it.tankId == userTank.id }!!
val tank = totalList.firstOrNull { it.tankId == userTank.tankId }!! supportList.add(
supportList.add( TankWithNationAndLevel(tank.tankId, tank.name, tank.price, tank.miniature,
TankWithNationAndLevel(tank.tankId, tank.name, tank.price, tank.miniature, totalLevelList.first { it.uid == tank.levelId }.level,
totalLevelList.first { it.uid == tank.levelId }.level, totalNationList.first { it.uid == tank.nationId }.nationName)
totalNationList.first { it.uid == tank.nationId }.nationName) )
)
}
} }
return supportList return supportList
} }
@@ -101,8 +107,8 @@ class RestTankRepository (
service.insertTank(tank.toRemote()) service.insertTank(tank.toRemote())
} }
override suspend fun buyTank(tankId: Long, userId: Long) { override suspend fun buyTank(tankId: Long, userId: Long, date: Date) {
service.insertUserTankCrossRef(UserTankCrossRefRemote(userId, tankId)) service.insertUserTankCrossRef(UserTankCrossRefRemote(userId, tankId, date))
} }
override suspend fun insertMany(tankList: List<Tank>) { override suspend fun insertMany(tankList: List<Tank>) {

View File

@@ -25,6 +25,7 @@ import ru.ulstu.`is`.pmu.tank.model.TankImage
import ru.ulstu.`is`.pmu.tank.model.User import ru.ulstu.`is`.pmu.tank.model.User
import ru.ulstu.`is`.pmu.tank.model.UserRole import ru.ulstu.`is`.pmu.tank.model.UserRole
import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef
import java.util.Date
//тут, собственно говоря, всё и мутится с БД :))) //тут, собственно говоря, всё и мутится с БД :)))
@Database( @Database(
@@ -51,7 +52,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun remoteKeysDao(): RemoteKeysDao abstract fun remoteKeysDao(): RemoteKeysDao
companion object { companion object {
private const val DB_NAME: String = "22-db" private const val DB_NAME: String = "23-db"
@Volatile @Volatile
private var INSTANCE: AppDatabase? = null private var INSTANCE: AppDatabase? = null
@@ -150,11 +151,11 @@ abstract class AppDatabase : RoomDatabase() {
userDao.insert(user) userDao.insert(user)
userDao.insert(UserTankCrossRef(user.userId, tank1.tankId ?: 0)) userDao.insert(UserTankCrossRef(user.userId, tank1.tankId ?: 0, Date()))
userDao.insert(UserTankCrossRef(user.userId, tank3.tankId ?: 0)) userDao.insert(UserTankCrossRef(user.userId, tank3.tankId ?: 0, Date()))
userDao.insert(UserTankCrossRef(user.userId, tank5.tankId ?: 0)) userDao.insert(UserTankCrossRef(user.userId, tank5.tankId ?: 0, Date()))
userDao.insert(UserTankCrossRef(user.userId, tank7.tankId ?: 0)) userDao.insert(UserTankCrossRef(user.userId, tank7.tankId ?: 0, Date()))
userDao.insert(UserTankCrossRef(user.userId, tank9.tankId ?: 0)) userDao.insert(UserTankCrossRef(user.userId, tank9.tankId ?: 0, Date()))
} }
} }

View File

@@ -0,0 +1,22 @@
package ru.ulstu.`is`.pmu.tank.database
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.text.SimpleDateFormat
import java.util.Date
@Serializer(forClass = Date::class)
object DateSerializer : KSerializer<Date> {
private val dateFormat =
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") // Adjust the format as needed
override fun serialize(encoder: Encoder, value: Date) {
encoder.encodeString(dateFormat.format(value))
}
override fun deserialize(decoder: Decoder): Date {
return dateFormat.parse(decoder.decodeString()) ?: Date()
}
}

View File

@@ -3,13 +3,9 @@ package ru.ulstu.`is`.pmu.tank.model
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Ignore import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.application.ui.getEmptyBitmap import com.application.ui.getEmptyBitmap
import ru.ulstu.`is`.pmu.R
import ru.ulstu.`is`.pmu.tank.database.Converters
@Entity( @Entity(
tableName = "tanks", tableName = "tanks",

View File

@@ -1,11 +1,13 @@
package ru.ulstu.`is`.pmu.tank.model package ru.ulstu.`is`.pmu.tank.model
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.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.NotNull
import java.util.Date
//many to many for user and tank //many to many for user and tank
@Entity( @Entity(
@@ -14,14 +16,37 @@ import org.jetbrains.annotations.NotNull
) )
data class UserTankCrossRef( data class UserTankCrossRef(
val userId: Long, val userId: Long,
val tankId: Long val tankId: Long,
@ColumnInfo(name = "date")
val date: Date,
){ ){
companion object { companion object {
fun getEmpty(): UserTankCrossRef { fun getEmpty(): UserTankCrossRef {
return UserTankCrossRef( return UserTankCrossRef(
userId = 0, userId = 0,
tankId = 0 tankId = 0,
date = Date()
) )
} }
} }
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as UserTankCrossRef
if (userId != other.userId) return false
if (tankId != other.tankId) return false
if (date != other.date) return false
return true
}
override fun hashCode(): Int {
var result = userId.hashCode()
result = 31 * result + tankId.hashCode()
result = 31 * result + date.hashCode()
return result
}
} }

View File

@@ -10,6 +10,7 @@ import ru.ulstu.`is`.pmu.tank.model.TankExtra
import ru.ulstu.`is`.pmu.tank.model.TankImage import ru.ulstu.`is`.pmu.tank.model.TankImage
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef
import java.util.Date
class OfflineTankRepository( class OfflineTankRepository(
private val tankDao: TankDao, private val tankDao: TankDao,
@@ -27,8 +28,8 @@ class OfflineTankRepository(
tankDao.insert(tank) tankDao.insert(tank)
} }
override suspend fun buyTank(tankId: Long, userId: Long) { override suspend fun buyTank(tankId: Long, userId: Long, date: Date) {
usersTanksDao.insert(UserTankCrossRef(userId, tankId)) usersTanksDao.insert(UserTankCrossRef(userId, tankId, date))
} }
override suspend fun insertMany(tankList: List<Tank>) { override suspend fun insertMany(tankList: List<Tank>) {

View File

@@ -5,6 +5,7 @@ import kotlinx.coroutines.flow.Flow
import ru.ulstu.`is`.pmu.tank.model.Tank import ru.ulstu.`is`.pmu.tank.model.Tank
import ru.ulstu.`is`.pmu.tank.model.TankExtra import ru.ulstu.`is`.pmu.tank.model.TankExtra
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
import java.util.Date
interface TankRepository { interface TankRepository {
suspend fun getAll(): List<Tank> suspend fun getAll(): List<Tank>
@@ -12,7 +13,7 @@ interface TankRepository {
suspend fun getTank(uid: Long): Tank suspend fun getTank(uid: Long): Tank
suspend fun getUserTanks(uid: Long): List<TankWithNationAndLevel> suspend fun getUserTanks(uid: Long): List<TankWithNationAndLevel>
suspend fun insertTank(tank: Tank, image: Bitmap) suspend fun insertTank(tank: Tank, image: Bitmap)
suspend fun buyTank(tankId: Long, userId: Long) suspend fun buyTank(tankId: Long, userId: Long, date: Date)
suspend fun insertMany(tankList: List<Tank>) suspend fun insertMany(tankList: List<Tank>)
suspend fun updateTank(tank: Tank, image: Bitmap) suspend fun updateTank(tank: Tank, image: Bitmap)
suspend fun deleteTank(tank: Tank) suspend fun deleteTank(tank: Tank)

View File

@@ -189,20 +189,59 @@
"password": "Tujh2003", "password": "Tujh2003",
"role": 1, "role": 1,
"balance": 12300000 "balance": 12300000
},
{
"id": 101,
"nickname": "KorbenDallas",
"email": "korbenDetka@mail.ru",
"password": "Korben2013",
"role": 0,
"balance": 11223300
},
{
"id": 102,
"nickname": "Amway921",
"email": "amway@mail.ru",
"password": "Amway2013",
"role": 0,
"balance": 11223300
} }
], ],
"users_tanks": [ "users_tanks": [
{ {
"id": 100, "id": 100,
"tankId": 1 "tankId": 1,
"date": "2023-12-12T12:06:14.720+0000"
}, },
{ {
"id": 100, "id": 100,
"tankId": 5 "tankId": 5,
"date": "2023-12-13T12:06:14.720+0000"
}, },
{ {
"id": 100, "id": 100,
"tankId": 6 "tankId": 6,
"date": "2023-12-14T12:06:14.720+0000"
},
{
"id": 101,
"tankId": 6,
"date": "2023-12-15T12:06:14.720+0000"
},
{
"id": 101,
"tankId": 11,
"date": "2023-12-16T12:06:14.720+0000"
},
{
"id": 102,
"tankId": 6,
"date": "2023-12-17T12:06:14.720+0000"
},
{
"id": 102,
"tankId": 1,
"date": "2023-12-18T12:06:14.720+0000"
} }
] ]
} }

View File

@@ -1,11 +1,11 @@
{ {
"name": "fake-db", "name": "20-db",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "fake-db", "name": "20-db",
"version": "1.0.0", "version": "1.0.0",
"devDependencies": { "devDependencies": {
"json-server": "0.17.4" "json-server": "0.17.4"