feat: updated DTO

This commit is contained in:
MaximOksiuta
2025-11-22 14:47:34 +03:00
parent 5084dedf90
commit 064157bf2c
10 changed files with 235 additions and 24 deletions
@@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "3e896e9a3d3b2f61149f8c0fde7e5964", "identityHash": "b16cf19ddaafa74ea796a48650e53014",
"entities": [ "entities": [
{ {
"tableName": "users", "tableName": "users",
@@ -55,7 +55,7 @@
}, },
{ {
"tableName": "resumes", "tableName": "resumes",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `experience_type` TEXT NOT NULL, `about_me` TEXT NOT NULL, `key_skills` TEXT NOT NULL, `position` TEXT NOT NULL, `from_salary` INTEGER, `to_salary` INTEGER, `recommended_skills` TEXT NOT NULL, PRIMARY KEY(`id`))", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `experience_type` TEXT NOT NULL, `about_me` TEXT NOT NULL, `key_skills` TEXT NOT NULL, `position` TEXT NOT NULL, `from_salary` INTEGER, `to_salary` INTEGER, `recommended_skills` TEXT NOT NULL, `city` TEXT NOT NULL, `experience` TEXT NOT NULL, `education` TEXT NOT NULL, `projects` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@@ -102,6 +102,30 @@
"columnName": "recommended_skills", "columnName": "recommended_skills",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
},
{
"fieldPath": "city",
"columnName": "city",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "experience",
"columnName": "experience",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "education",
"columnName": "education",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "projects",
"columnName": "projects",
"affinity": "TEXT",
"notNull": true
} }
], ],
"primaryKey": { "primaryKey": {
@@ -114,7 +138,7 @@
], ],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3e896e9a3d3b2f61149f8c0fde7e5964')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b16cf19ddaafa74ea796a48650e53014')"
] ]
} }
} }
@@ -2,9 +2,11 @@ package com.prodhack.moscow2025.data.data_providers.local_db
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.prodhack.moscow2025.data.data_providers.local_db.dao.CleanUpDao import com.prodhack.moscow2025.data.data_providers.local_db.dao.CleanUpDao
import com.prodhack.moscow2025.data.data_providers.local_db.dao.ResumeDao import com.prodhack.moscow2025.data.data_providers.local_db.dao.ResumeDao
import com.prodhack.moscow2025.data.data_providers.local_db.dao.UserDao import com.prodhack.moscow2025.data.data_providers.local_db.dao.UserDao
import com.prodhack.moscow2025.data.data_providers.local_db.entities.JsonTypeConverters
import com.prodhack.moscow2025.data.data_providers.local_db.entities.ResumeEntity import com.prodhack.moscow2025.data.data_providers.local_db.entities.ResumeEntity
import com.prodhack.moscow2025.data.data_providers.local_db.entities.UserEntity import com.prodhack.moscow2025.data.data_providers.local_db.entities.UserEntity
@@ -13,6 +15,7 @@ import com.prodhack.moscow2025.data.data_providers.local_db.entities.UserEntity
version = 1, version = 1,
exportSchema = true exportSchema = true
) )
@TypeConverters(JsonTypeConverters::class)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao abstract fun userDao(): UserDao
@@ -0,0 +1,49 @@
package com.prodhack.moscow2025.data.data_providers.local_db.entities
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.prodhack.moscow2025.domain.models.Education
import com.prodhack.moscow2025.domain.models.Project
import com.prodhack.moscow2025.domain.models.WorkExperience
object JsonTypeConverters {
private val gson = Gson()
@TypeConverter
fun fromWorkExperienceList(value: List<WorkExperience>): String {
val type = object : TypeToken<List<WorkExperience>>() {}.type
return gson.toJson(value, type)
}
@TypeConverter
fun toWorkExperienceList(value: String): List<WorkExperience> {
val type = object : TypeToken<List<WorkExperience>>() {}.type
return gson.fromJson(value, type)
}
@TypeConverter
fun fromEducationList(value: List<Education>): String {
val type = object : TypeToken<List<Education>>() {}.type
return gson.toJson(value, type)
}
@TypeConverter
fun toEducationList(value: String): List<Education> {
val type = object : TypeToken<List<Education>>() {}.type
return gson.fromJson(value, type)
}
@TypeConverter
fun fromProjectList(value: List<Project>): String {
val type = object : TypeToken<List<Project>>() {}.type
return gson.toJson(value, type)
}
@TypeConverter
fun toProjectList(value: String): List<Project> {
val type = object : TypeToken<List<Project>>() {}.type
return gson.fromJson(value, type)
}
}
@@ -5,7 +5,6 @@ import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.prodhack.moscow2025.domain.models.ExperienceType import com.prodhack.moscow2025.domain.models.ExperienceType
import com.prodhack.moscow2025.domain.models.ResumeModel import com.prodhack.moscow2025.domain.models.ResumeModel
import kotlin.math.exp
@Entity(tableName = "resumes") @Entity(tableName = "resumes")
data class ResumeEntity( data class ResumeEntity(
@@ -23,7 +22,11 @@ data class ResumeEntity(
@ColumnInfo("to_salary") @ColumnInfo("to_salary")
val toSalary: Int?, val toSalary: Int?,
@ColumnInfo("recommended_skills") @ColumnInfo("recommended_skills")
val recommendedSkills: String val recommendedSkills: String,
val city: String,
val experience: String, // Store as JSON string, requires TypeConverter
val education: String, // Store as JSON string, requires TypeConverter
val projects: String // Store as JSON string, requires TypeConverter
) { ) {
fun mapToDomain(): ResumeModel = ResumeModel( fun mapToDomain(): ResumeModel = ResumeModel(
id = id, id = id,
@@ -32,6 +35,10 @@ data class ResumeEntity(
experienceType = ExperienceType.valueOf(experienceType), experienceType = ExperienceType.valueOf(experienceType),
skills = keySkills.split("|"), skills = keySkills.split("|"),
prediction = Pair(fromSalary, toSalary), prediction = Pair(fromSalary, toSalary),
recommendedSkills = recommendedSkills.split("|") recommendedSkills = recommendedSkills.split("|"),
city = city,
experience = JsonTypeConverters.toWorkExperienceList(experience),
education = JsonTypeConverters.toEducationList(education),
projects = JsonTypeConverters.toProjectList(projects)
) )
} }
@@ -1,9 +1,13 @@
package com.prodhack.moscow2025.data.dto package com.prodhack.moscow2025.data.dto
import com.prodhack.moscow2025.data.data_providers.local_db.entities.JsonTypeConverters
import com.prodhack.moscow2025.data.data_providers.local_db.entities.ResumeEntity import com.prodhack.moscow2025.data.data_providers.local_db.entities.ResumeEntity
import com.prodhack.moscow2025.domain.models.Education
import com.prodhack.moscow2025.domain.models.EducationGrades
import com.prodhack.moscow2025.domain.models.ExperienceType import com.prodhack.moscow2025.domain.models.ExperienceType
import com.prodhack.moscow2025.domain.models.ResumeCreationModel import com.prodhack.moscow2025.domain.models.Project
import com.prodhack.moscow2025.domain.models.ResumeModel import com.prodhack.moscow2025.domain.models.ResumeModel
import com.prodhack.moscow2025.domain.models.WorkExperience
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -51,6 +55,10 @@ data class ResumeDTO(
@SerialName("key_skills") @SerialName("key_skills")
val keySkills: List<String>, val keySkills: List<String>,
val position: String, val position: String,
val city: String,
val experience: List<ExperienceDTO>,
val education: List<EducationDTO>,
val project: List<ProjectDTO>,
val prediction: PredictionDTO val prediction: PredictionDTO
) { ) {
fun mapToDomain(): ResumeModel = ResumeModel( fun mapToDomain(): ResumeModel = ResumeModel(
@@ -63,7 +71,11 @@ data class ResumeDTO(
prediction.fromSalary.toIntOrNull(), prediction.fromSalary.toIntOrNull(),
prediction.toSalary.toIntOrNull() prediction.toSalary.toIntOrNull()
), ),
recommendedSkills = prediction.recommendedSkills recommendedSkills = prediction.recommendedSkills,
city = city,
experience = experience.map { it.mapToDomain() },
education = education.map { it.mapToDomain() },
projects = project.map { it.mapToDomain() }
) )
fun mapToDB(): ResumeEntity = ResumeEntity( fun mapToDB(): ResumeEntity = ResumeEntity(
@@ -74,25 +86,97 @@ data class ResumeDTO(
fromSalary = prediction.fromSalary.toIntOrNull(), fromSalary = prediction.fromSalary.toIntOrNull(),
toSalary = prediction.toSalary.toIntOrNull(), toSalary = prediction.toSalary.toIntOrNull(),
recommendedSkills = prediction.recommendedSkills.joinToString("|"), recommendedSkills = prediction.recommendedSkills.joinToString("|"),
experienceType = experienceType.mapToDomain().name experienceType = experienceType.mapToDomain().name,
city = city,
experience = JsonTypeConverters.fromWorkExperienceList(experience.map { it.mapToDomain() }),
education = JsonTypeConverters.fromEducationList(education.map { it.mapToDomain() }),
projects = JsonTypeConverters.fromProjectList(project.map { it.mapToDomain() }),
)
}
@Serializable
data class ExperienceDTO(
val place: String,
val description: String,
@SerialName("month_duration")
val monthDuration: Int,
) {
fun mapToDomain(): WorkExperience = WorkExperience(
place = place,
description = description,
monthDuration = monthDuration
)
}
@Serializable
data class EducationDTO(
val place: String,
val grade: EducationGradesDTO,
val specialization: String,
val description: String
) {
fun mapToDomain(): Education = Education(
place = place,
grade = grade.mapToDomain(),
specialization = specialization,
description = description
)
}
@Serializable
enum class EducationGradesDTO {
@SerialName("common")
Common,
@SerialName("middle")
Middle,
@SerialName("middle_spec")
MiddleSpec,
@SerialName("high_not_finished")
HighNotFinished,
@SerialName("high")
High,
@SerialName("additional")
Additional;
fun mapToDomain(): EducationGrades = when (this) {
Common -> EducationGrades.Common
Middle -> EducationGrades.Middle
MiddleSpec -> EducationGrades.MiddleSpec
HighNotFinished -> EducationGrades.HighNotFinished
High -> EducationGrades.High
Additional -> EducationGrades.Additional
}
}
@Serializable
data class ProjectDTO(
val name: String,
val description: String
) {
fun mapToDomain(): Project = Project(
name = name,
description = description
) )
} }
@Serializable @Serializable
data class ResumeCreateDTO( data class ResumeCreateDTO(
@SerialName("about_me")
val aboutMe: String,
@SerialName("experience_type") @SerialName("experience_type")
val experienceType: ExperienceTypeDTO, val experienceType: ExperienceTypeDTO,
@SerialName("about_me")
val aboutMe: String,
@SerialName("key_skills")
val keySkills: List<String>, val keySkills: List<String>,
val position: String val position: String,
) val city: String,
val experience: List<ExperienceDTO>,
fun ResumeCreationModel.mapToData(): ResumeCreateDTO = ResumeCreateDTO( val education: List<EducationDTO>,
aboutMe = about, val project: List<ProjectDTO>,
experienceType = experienceType.mapToData(),
keySkills = skills,
position = position
) )
@Serializable @Serializable
@@ -5,7 +5,11 @@ data class ResumeModel(
val position: String, val position: String,
val about: String, val about: String,
val skills: List<String>, val skills: List<String>,
val city: String,
val experienceType: ExperienceType, val experienceType: ExperienceType,
val experience: List<WorkExperience>,
val education: List<Education>,
val projects: List<Project>,
val prediction: Pair<Int?, Int?>, val prediction: Pair<Int?, Int?>,
val recommendedSkills: List<String> val recommendedSkills: List<String>
) )
@@ -14,7 +18,38 @@ data class ResumeCreationModel(
val position: String, val position: String,
val about: String, val about: String,
val skills: List<String>, val skills: List<String>,
val experienceType: ExperienceType val city: String?,
val experienceType: ExperienceType,
val experience: List<WorkExperience>,
val education: List<Education>,
val projects: List<Project>
)
data class WorkExperience(
val place: String,
val description: String,
val monthDuration: Int
)
data class Education(
val place: String,
val grade: EducationGrades,
val specialization: String,
val description: String
)
enum class EducationGrades {
Common,
Middle,
MiddleSpec,
HighNotFinished,
High,
Additional
}
data class Project(
val name: String,
val description: String
) )
enum class ExperienceType { enum class ExperienceType {
@@ -7,6 +7,7 @@ import org.koin.core.annotation.Single
@Single @Single
class CreateResumeUseCase( class CreateResumeUseCase(
private val resumeRepository: ResumeRepository private val resumeRepository: ResumeRepository
){ ) {
suspend operator fun invoke(resumeForm: ResumeCreationModel): Result<String> = resumeRepository.createResume(resumeForm) suspend operator fun invoke(resumeForm: ResumeCreationModel): Result<String> =
resumeRepository.createResume(resumeForm)
} }
@@ -30,6 +30,10 @@ class LoadResumeListUseCase(private val resumeRepository: ResumeRepository) {
"Ktor" "Ktor"
), ),
experienceType = ExperienceType.Between3And6, experienceType = ExperienceType.Between3And6,
city = "Moscow",
experience = listOf(),
education = listOf(),
projects = listOf(),
prediction = Pair(200000, 230000), prediction = Pair(200000, 230000),
recommendedSkills = listOf("KMP") recommendedSkills = listOf("KMP")
) )
@@ -154,7 +154,7 @@ fun CreateResumeScreen(
Spacer(modifier = Modifier.height(Paddings.large)) Spacer(modifier = Modifier.height(Paddings.large))
BigButton( BigButton(
onClick = {}, onClick = viewModel::submit,
buttonText = "Узнать свою ЗП", buttonText = "Узнать свою ЗП",
isLoading = viewModel.resumeFillState.collectAsState().value.isLoading isLoading = viewModel.resumeFillState.collectAsState().value.isLoading
) )
@@ -138,7 +138,11 @@ class CreateResumeViewModel(
position = position, position = position,
about = about, about = about,
skills = keySkills.toList(), skills = keySkills.toList(),
experienceType = experience!!.mapToDomain() experienceType = experience!!.mapToDomain(),
city = null,
experience = emptyList(),
education = emptyList(),
projects = emptyList()
) )
} }