Заготовка

This commit is contained in:
Халимов Рустам
2026-05-07 23:27:37 +03:00
parent 2d2e4b685e
commit 9daa786cfb
33 changed files with 1662 additions and 178 deletions
@@ -0,0 +1,33 @@
package chats.data.local.database
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import chats.data.local.dao.ChatDao
import chats.data.local.dao.MessageDao
import chats.data.local.dao.UserProfileDao
/**
* Room Database для локального хранения данных мессенджера
* Реализует паттерн Single Source of Truth
*/
@Database(
entities = [
MessageEntity::class,
ChatEntity::class,
UserProfileEntity::class
],
version = 1,
exportSchema = false
)
@TypeConverters(MessageStatusConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun messageDao(): MessageDao
abstract fun chatDao(): ChatDao
abstract fun userProfileDao(): UserProfileDao
companion object {
const val DATABASE_NAME = "knot_messenger.db"
}
}
@@ -0,0 +1,37 @@
package chats.data.local.database
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
/**
* Entity для хранения чатов в локальной БД Room
*/
@Entity(
tableName = "chats",
indices = [
Index(value = ["remoteId"], unique = true)
]
)
data class ChatEntity(
@PrimaryKey(autoGenerate = false)
val localId: String,
val remoteId: String?, // ID с сервера
val type: String, // PRIVATE, GROUP, CHANNEL
val name: String,
val avatar: String? = null,
val unreadCount: Int = 0,
val lastMessageId: String? = null,
val lastMessageText: String? = null,
val lastMessageTimestamp: String? = null,
val updatedAtMillis: Long = System.currentTimeMillis()
)
@@ -0,0 +1,66 @@
package chats.data.local.database
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import chats.domain.model.MessageStatus
/**
* Entity для хранения сообщений в локальной БД Room
* Используется для офлайн-режима и Single Source of Truth паттерна
*/
@Entity(
tableName = "messages",
indices = [
Index(value = ["chatId", "sequenceId"]),
Index(value = ["localId"], unique = true),
Index(value = ["serverId"]),
Index(value = ["status"])
]
)
data class MessageEntity(
@PrimaryKey(autoGenerate = false)
val localId: String, // UUID генерируется на клиенте при создании
val serverId: String?, // null для неотправленных сообщений
val idempotencyKey: String, // UUID v4 для идемпотентности
val chatId: String,
val senderId: String,
val senderName: String,
val senderAvatar: String? = null,
val content: String?,
val sequenceId: Long,
val createdAt: String, // ISO-8601 формат
val mediaType: String,
val mediaJson: String, // JSON список медиа (для Room)
val reactionsJson: String = "{}", // JSON map эмодзи -> count
val status: MessageStatus,
val isPinned: Boolean = false,
val isForwarded: Boolean = false,
val forwardedFromName: String? = null,
val replyToServerId: String? = null,
val errorMessage: String? = null,
val retryCount: Int = 0,
val createdAtMillis: Long = 0,
val updatedAtMillis: Long = 0
)
@@ -0,0 +1,17 @@
package chats.data.local.database
import androidx.room.TypeConverter
import chats.domain.model.MessageStatus
/**
* Конвертеры для Room для работы с Enum и другими типами
*/
class MessageStatusConverter {
@TypeConverter
fun fromMessageStatus(status: MessageStatus): String = status.name
@TypeConverter
fun toMessageStatus(value: String): MessageStatus = runCatching {
MessageStatus.valueOf(value)
}.getOrDefault(MessageStatus.UNKNOWN)
}
@@ -0,0 +1,27 @@
package chats.data.local.database
import androidx.room.Entity
import androidx.room.PrimaryKey
/**
* Entity для хранения профиля пользователя в локальной БД Room
*/
@Entity(tableName = "user_profile")
data class UserProfileEntity(
@PrimaryKey
val userId: String,
val username: String,
val displayName: String,
val avatarUrl: String? = null,
val bio: String? = null,
val isOnline: Boolean = false,
val lastSeenMillis: Long = 0,
val updatedAtMillis: Long = System.currentTimeMillis()
)