diff --git a/Sources/src/main/kotlin/allin/Application.kt b/Sources/src/main/kotlin/allin/Application.kt index 826268a..0de0d66 100644 --- a/Sources/src/main/kotlin/allin/Application.kt +++ b/Sources/src/main/kotlin/allin/Application.kt @@ -1,13 +1,7 @@ package allin -import allin.entities.BetEntity -import allin.entities.BetsEntity -import allin.entities.ResponsesEntity -import allin.entities.UsersEntity -import allin.routing.BasicRouting -import allin.routing.BetRouter -import allin.routing.ParticipationRouter -import allin.routing.UserRouter +import allin.entities.* +import allin.routing.* import allin.utils.* import com.typesafe.config.ConfigFactory import io.ktor.serialization.kotlinx.json.* @@ -54,7 +48,9 @@ private fun Application.extracted() { UserRouter() BetRouter() ParticipationRouter() + BetDetailRouter() UsersEntity.createUserTable() BetsEntity.createBetsTable() ResponsesEntity.createResponseTable() + ParticipationsEntity.createParticipationTable() } diff --git a/Sources/src/main/kotlin/allin/dto/UserDTO.kt b/Sources/src/main/kotlin/allin/dto/UserDTO.kt index c0bec1d..2ed2a42 100644 --- a/Sources/src/main/kotlin/allin/dto/UserDTO.kt +++ b/Sources/src/main/kotlin/allin/dto/UserDTO.kt @@ -1,4 +1,4 @@ package allin.dto import kotlinx.serialization.Serializable @Serializable -data class UserDTO(val id: String, val username: String, val email: String, val nbCoins: Double, var token:String?) +data class UserDTO(val id: String, val username: String, val email: String, val nbCoins: Int, var token:String?) diff --git a/Sources/src/main/kotlin/allin/entities/ParticipationEntity.kt b/Sources/src/main/kotlin/allin/entities/ParticipationEntity.kt new file mode 100644 index 0000000..5684f8f --- /dev/null +++ b/Sources/src/main/kotlin/allin/entities/ParticipationEntity.kt @@ -0,0 +1,90 @@ +package allin.entities + +import allin.database +import allin.model.Participation +import allin.utils.Execute +import org.ktorm.dsl.* +import org.ktorm.entity.Entity +import org.ktorm.schema.* +import java.util.* + +interface ParticipationEntity : Entity { + val id: String + val betId: String + val username: String + val answer: String + val stake: Int +} + + +object ParticipationsEntity : Table("participation") { + val id = uuid("id").primaryKey() + val betId = uuid("bet") + val username = varchar("username") + val answer = varchar("answer") + val stake = int("stake") + + fun createParticipationTable(){ + val request="CREATE TABLE IF NOT EXISTS participation (id uuid PRIMARY KEY,bet uuid,username varchar(250),answer varchar(250),stake int);" + database.Execute(request) + } + + fun addParticipationEntity(participation : Participation){ + database.insert(ParticipationsEntity){ + set(it.id, UUID.fromString(participation.id)) + set(it.betId,UUID.fromString(participation.betId)) + set(it.username,participation.username) + set(it.answer,participation.answer) + set(it.stake,participation.stake) + } + } + + fun getParticipationEntityFromBetId(betid: String): MutableList { + return database.from(ParticipationsEntity) + .select() + .where { betId eq UUID.fromString(betid) } + .map { row -> + Participation( + row[id].toString(), + row[betId].toString(), + row[username].toString(), + row[answer].toString(), + row[stake] ?: 0, + ) + }.toMutableList() + } + + fun getParticipationEntityFromUserId(user: String, betid: String): MutableList { + return database.from(ParticipationsEntity) + .select() + .where { (betId eq UUID.fromString(betid)) and (username eq user) } + .map { row -> + Participation( + row[id].toString(), + row[betId].toString(), + row[username].toString(), + row[answer].toString(), + row[stake] ?: 0, + ) + }.toMutableList() + } + + fun getParticipationEntity(): MutableList { + return database.from(ParticipationsEntity).select().map { + row -> Participation( + row[id].toString(), + row[betId].toString(), + row[username].toString(), + row[answer].toString(), + row[stake]?:0, + ) + }.toMutableList() + } + + fun deleteParticipation(participation: Participation): Boolean { + val deletedCount = database.delete(ParticipationsEntity) { + it.id eq UUID.fromString(participation.id) + } + return deletedCount > 0 + } +} \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/entities/UserEntity.kt b/Sources/src/main/kotlin/allin/entities/UserEntity.kt index 9a1bcfc..9e1614f 100644 --- a/Sources/src/main/kotlin/allin/entities/UserEntity.kt +++ b/Sources/src/main/kotlin/allin/entities/UserEntity.kt @@ -7,20 +7,19 @@ import allin.utils.Execute import org.ktorm.dsl.* import org.ktorm.entity.* import org.ktorm.schema.* -import java.util.* import java.util.UUID.fromString interface UserEntity : Entity { val username: String var email: String var password: String - var nbCoins: Double + var nbCoins: Int } object UsersEntity : Table("utilisateur") { val id = uuid("id").primaryKey() val username = varchar("username") val password = varchar("password") - val nbCoins = double("coins") + val nbCoins = int("coins") val email = varchar("email") fun getUserToUserDTO(): MutableList { @@ -29,7 +28,7 @@ object UsersEntity : Table("utilisateur") { row[id].toString(), row[username].toString(), row[email].toString(), - row[nbCoins]?:0.0, + row[nbCoins]?:0, null ) }.toMutableList() @@ -40,6 +39,11 @@ object UsersEntity : Table("utilisateur") { database.Execute(request) } + fun modifyCoins(user: String, cost : Int){ + val request = "UPDATE utilisateur SET coins = coins - $cost WHERE username = '$user';" + database.Execute(request) + + } fun getUserByUsernameAndPassword(login: String): Pair { return database.from(UsersEntity) @@ -51,7 +55,7 @@ object UsersEntity : Table("utilisateur") { row[id].toString(), row[username].toString(), row[email].toString(), - row[nbCoins] ?: 0.0, + row[nbCoins]?:0, null ), row[password].toString() diff --git a/Sources/src/main/kotlin/allin/model/BetAction.kt b/Sources/src/main/kotlin/allin/model/BetAction.kt deleted file mode 100644 index 2f2298b..0000000 --- a/Sources/src/main/kotlin/allin/model/BetAction.kt +++ /dev/null @@ -1,5 +0,0 @@ -package allin.model - -import allin.dto.UserDTO -data class BetAction(val id:Int, val coins: Int, val user: String, val bet: Int) -data class BetActionCompleted(val id:Int, val coins: Int, val user: UserDTO, val bet: Bet) diff --git a/Sources/src/main/kotlin/allin/model/BetDetail.kt b/Sources/src/main/kotlin/allin/model/BetDetail.kt new file mode 100644 index 0000000..757b297 --- /dev/null +++ b/Sources/src/main/kotlin/allin/model/BetDetail.kt @@ -0,0 +1,34 @@ +package allin.model + +import kotlinx.serialization.Serializable + +@Serializable +data class BetAnswerDetail( + val response: String, // La réponse (ex: "Yes", "No" etc...) + val totalStakes: Int, // Le nombre total d'argent misé sur cette réponse + val totalParticipants: Int, // Le nombre total de participant + val highestStake: Int, // Plus grosse mise + val odds: Float // Cote du bet +) + +@Serializable +data class BetDetail( + val bet: Bet, // Le Bet + val answers: List?, // Pour chaque réponse possible du bet les détails + val participations: List?, // La liste des participations + val userParticipation: Participation? // La participation du User current +) + +fun getBetAnswerDetail(bet: Bet, participations: List): List { + return bet.response.map { response -> + val responseParticipations = participations.filter { it.answer == response } + BetAnswerDetail( + response = response, + totalStakes = responseParticipations.sumOf { it.stake }, + totalParticipants = responseParticipations.size, + highestStake = responseParticipations.maxOfOrNull { it.stake } ?: 0, + odds = if (participations.isEmpty()) 1f else responseParticipations.size / participations.size.toFloat() + ) + } + +} diff --git a/Sources/src/main/kotlin/allin/routing/BetActionRouter.kt b/Sources/src/main/kotlin/allin/routing/BetActionRouter.kt deleted file mode 100644 index bd91f03..0000000 --- a/Sources/src/main/kotlin/allin/routing/BetActionRouter.kt +++ /dev/null @@ -1,11 +0,0 @@ -package allin.routing - -import io.ktor.server.application.* -import io.ktor.server.routing.* - -fun Application.BetActionRouter(){ - routing { - route("/BetAction/add"){ - } - } -} diff --git a/Sources/src/main/kotlin/allin/routing/BetDetailRouter.kt b/Sources/src/main/kotlin/allin/routing/BetDetailRouter.kt new file mode 100644 index 0000000..8596f34 --- /dev/null +++ b/Sources/src/main/kotlin/allin/routing/BetDetailRouter.kt @@ -0,0 +1,44 @@ +package allin.routing + +import allin.entities.BetsEntity.getBets +import allin.entities.ParticipationsEntity.getParticipationEntityFromBetId +import allin.entities.ParticipationsEntity.getParticipationEntityFromUserId +import allin.ext.hasToken +import allin.ext.verifyUserFromToken +import allin.model.BetDetail +import allin.model.getBetAnswerDetail +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.response.* +import io.ktor.server.routing.* + +fun Application.BetDetailRouter() { + routing { + authenticate { + get("/betdetail/get/{id}") { + hasToken { principal -> + verifyUserFromToken(principal) { user, _ -> + val id = call.parameters["id"].toString() + val participations = getParticipationEntityFromBetId(id) + val selectedBet = getBets().find { it.id == id } + if (selectedBet != null) { + call.respond( + HttpStatusCode.Accepted, + BetDetail( + selectedBet, + getBetAnswerDetail(selectedBet, participations), + participations.toList(), + getParticipationEntityFromUserId(user.username, id).lastOrNull() + ) + ) + } else { + call.respond(HttpStatusCode.NotFound, "Bet not found") + } + } + } + } + } + } +} + diff --git a/Sources/src/main/kotlin/allin/routing/BetRouter.kt b/Sources/src/main/kotlin/allin/routing/BetRouter.kt index 619cb55..7f7db78 100644 --- a/Sources/src/main/kotlin/allin/routing/BetRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/BetRouter.kt @@ -2,6 +2,7 @@ package allin.routing import allin.entities.BetsEntity.addBetEntity import allin.entities.BetsEntity.getBets +import allin.entities.ParticipationsEntity.getParticipationEntity import allin.ext.hasToken import allin.ext.verifyUserFromToken import allin.model.ApiMessage @@ -16,7 +17,6 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import java.util.* -//val bets = mutableListOf() val tokenManagerBet = AppConfig.tokenManager fun Application.BetRouter() { @@ -42,7 +42,6 @@ fun Application.BetRouter() { bet.response, username ) - //bets.add(betWithId) addBetEntity(betWithId) call.respond(HttpStatusCode.Created, betWithId) } @@ -100,7 +99,7 @@ fun Application.BetRouter() { val bets= getBets() hasToken { principal -> verifyUserFromToken(principal) { user, _ -> - val bets = participations + val bets = getParticipationEntity() .filter { it.username == user.username } .mapNotNull { itParticipation -> bets.find { it.id == itParticipation.betId } } call.respond(HttpStatusCode.OK, bets) diff --git a/Sources/src/main/kotlin/allin/routing/ParticipationRouter.kt b/Sources/src/main/kotlin/allin/routing/ParticipationRouter.kt index b56551e..acafa86 100644 --- a/Sources/src/main/kotlin/allin/routing/ParticipationRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/ParticipationRouter.kt @@ -1,5 +1,9 @@ package allin.routing +import allin.entities.ParticipationsEntity.addParticipationEntity +import allin.entities.ParticipationsEntity.deleteParticipation +import allin.entities.ParticipationsEntity.getParticipationEntity +import allin.entities.UsersEntity.modifyCoins import allin.ext.hasToken import allin.ext.verifyUserFromToken import allin.model.ApiMessage @@ -13,8 +17,6 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import java.util.* -val participations = mutableListOf() - fun Application.ParticipationRouter() { routing { authenticate { @@ -23,7 +25,7 @@ fun Application.ParticipationRouter() { val participation = call.receive() verifyUserFromToken(principal) { user, _ -> if (user.nbCoins >= participation.stake) { - participations.add( + addParticipationEntity( Participation( id = UUID.randomUUID().toString(), betId = participation.betId, @@ -32,6 +34,7 @@ fun Application.ParticipationRouter() { stake = participation.stake ) ) + modifyCoins(user.username,participation.stake) call.respond(HttpStatusCode.Created) } else { call.respond(HttpStatusCode.Forbidden, ApiMessage.NotEnoughCoins) @@ -42,10 +45,9 @@ fun Application.ParticipationRouter() { delete("/participations/delete") { hasToken { principal -> val participationId = call.receive() - participations.find { it.id == participationId }?.let { participation -> + getParticipationEntity().find { it.id == participationId }?.let { participation -> verifyUserFromToken(principal) { _, _ -> - // user.nbCoins += participation.stake - participations.remove(participation) + deleteParticipation(participation) call.respond(HttpStatusCode.NoContent) } } ?: call.respond(HttpStatusCode.NotFound, ApiMessage.ParticipationNotFound) diff --git a/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt b/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt index febe993..5ef9fdf 100644 --- a/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt +++ b/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt @@ -16,7 +16,7 @@ import java.time.format.DateTimeFormatter @Serializer(ZonedDateTime::class) object ZonedDateTimeSerializer : KSerializer { - private val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z") + private val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z") override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ZonedDateTime", PrimitiveKind.STRING)