From 484bb758ae21542c67941c7ce86bfc2c6c4d78e4 Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Mon, 25 Mar 2024 03:06:28 -0400 Subject: [PATCH] [+] Wacca database models --- src/main/java/ext/Ext.kt | 39 ---- src/main/java/ext/Json.kt | 53 +++++ .../java/icu/samnyan/aqua/net/games/Models.kt | 12 +- .../samnyan/aqua/sega/general/model/Card.kt | 9 +- .../samnyan/aqua/sega/wacca/model/db/Repos.kt | 30 +++ .../aqua/sega/wacca/model/db/WaccaUser.kt | 57 +++++ .../sega/wacca/model/db/WaccaUserModels.kt | 147 ++++++++++++ .../mariadb/V1000_11__wacca_init.sql | 211 ++++++++++++++++++ 8 files changed, 510 insertions(+), 48 deletions(-) create mode 100644 src/main/java/ext/Json.kt create mode 100644 src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt create mode 100644 src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt create mode 100644 src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt create mode 100644 src/main/resources/db/migration/mariadb/V1000_11__wacca_init.sql diff --git a/src/main/java/ext/Ext.kt b/src/main/java/ext/Ext.kt index bd768ce0..0afffbc2 100644 --- a/src/main/java/ext/Ext.kt +++ b/src/main/java/ext/Ext.kt @@ -83,45 +83,6 @@ operator fun Int.minus(message: String): Nothing { val emailRegex = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$".toRegex() fun Str.isValidEmail(): Bool = emailRegex.matches(this) -// JSON -val ACCEPTABLE_FALSE = setOf("0", "false", "no", "off", "False", "None", "null") -val ACCEPTABLE_TRUE = setOf("1", "true", "yes", "on", "True") -val JSON_FUZZY_BOOLEAN = SimpleModule().addDeserializer(Boolean::class.java, object : JsonDeserializer() { - override fun deserialize(parser: JsonParser, context: DeserializationContext) = when(parser.text) { - in ACCEPTABLE_FALSE -> false - in ACCEPTABLE_TRUE -> true - else -> 400 - "Invalid boolean value ${parser.text}" - } -}) -val JSON_DATETIME = SimpleModule().addDeserializer(LocalDateTime::class.java, object : JsonDeserializer() { - override fun deserialize(parser: JsonParser, context: DeserializationContext) = - parser.text.asDateTime() ?: (400 - "Invalid date time value ${parser.text}") -}) -val JACKSON = ObjectMapper().apply { - setSerializationInclusion(JsonInclude.Include.NON_NULL) - setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL) - findAndRegisterModules() - registerModule(JSON_FUZZY_BOOLEAN) - registerModule(JSON_DATETIME) - configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) -} -inline fun ObjectMapper.readValue(str: Str) = readValue(str, T::class.java) -// TODO: https://stackoverflow.com/q/78197784/7346633 -inline fun Str.parseJackson() = if (contains("null")) { - val map = JACKSON.readValue>(this) - JACKSON.convertValue(map.recursiveNotNull(), T::class.java) -} -else JACKSON.readValue(this, T::class.java) -fun T.toJson() = JACKSON.writeValueAsString(this) -@OptIn(ExperimentalSerializationApi::class) -val JSON = Json { - ignoreUnknownKeys = true - isLenient = true - namingStrategy = JsonNamingStrategy.SnakeCase - explicitNulls = false - coerceInputValues = true -} -inline fun Json.parse(str: Str) = decodeFromString(str) // Global Tools val HTTP = HttpClient(CIO) { install(ContentNegotiation) { diff --git a/src/main/java/ext/Json.kt b/src/main/java/ext/Json.kt new file mode 100644 index 00000000..2c90f364 --- /dev/null +++ b/src/main/java/ext/Json.kt @@ -0,0 +1,53 @@ +package ext + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.* +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonNamingStrategy + +// Jackson +val ACCEPTABLE_FALSE = setOf("0", "false", "no", "off", "False", "None", "null") +val ACCEPTABLE_TRUE = setOf("1", "true", "yes", "on", "True") +val JSON_FUZZY_BOOLEAN = SimpleModule().addDeserializer(Boolean::class.java, object : JsonDeserializer() { + override fun deserialize(parser: JsonParser, context: DeserializationContext) = when(parser.text) { + in ACCEPTABLE_FALSE -> false + in ACCEPTABLE_TRUE -> true + else -> 400 - "Invalid boolean value ${parser.text}" + } +}) +val JSON_DATETIME = SimpleModule().addDeserializer(java.time.LocalDateTime::class.java, object : JsonDeserializer() { + override fun deserialize(parser: JsonParser, context: DeserializationContext) = + parser.text.asDateTime() ?: (400 - "Invalid date time value ${parser.text}") +}) +val JACKSON = jacksonObjectMapper().apply { + setSerializationInclusion(JsonInclude.Include.NON_NULL) + setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL) + findAndRegisterModules() + registerModule(JSON_FUZZY_BOOLEAN) + registerModule(JSON_DATETIME) + configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE); +} +inline fun ObjectMapper.readValue(str: Str) = readValue(str, T::class.java) +// TODO: https://stackoverflow.com/q/78197784/7346633 +inline fun Str.parseJackson() = if (contains("null")) { + val map = JACKSON.readValue>(this) + JACKSON.convertValue(map.recursiveNotNull(), T::class.java) +} +else JACKSON.readValue(this, T::class.java) +fun T.toJson() = JACKSON.writeValueAsString(this) + + +// KotlinX Serialization +@OptIn(ExperimentalSerializationApi::class) +val JSON = Json { + ignoreUnknownKeys = true + isLenient = true + namingStrategy = JsonNamingStrategy.SnakeCase + explicitNulls = false + coerceInputValues = true +} \ No newline at end of file diff --git a/src/main/java/icu/samnyan/aqua/net/games/Models.kt b/src/main/java/icu/samnyan/aqua/net/games/Models.kt index eebd7b15..64f4c0eb 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/Models.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/Models.kt @@ -3,9 +3,11 @@ package icu.samnyan.aqua.net.games import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonSerialize import ext.JACKSON import ext.JavaSerializable import icu.samnyan.aqua.sega.general.model.Card +import icu.samnyan.aqua.sega.util.jackson.AccessCodeSerializer import jakarta.persistence.* import kotlinx.serialization.Serializable import org.springframework.data.domain.Page @@ -104,7 +106,6 @@ interface IGenericGamePlaylog { val isAllPerfect: Boolean } -@Serializable @MappedSuperclass open class BaseEntity( @Id @@ -115,6 +116,15 @@ open class BaseEntity( override fun toString() = JACKSON.writeValueAsString(this) } +@MappedSuperclass +open class UserDataEntity : BaseEntity() { + @JsonSerialize(using = AccessCodeSerializer::class) + @JsonProperty(value = "accessCode", access = JsonProperty.Access.READ_ONLY) + @OneToOne + @JoinColumn(name = "aime_card_id", unique = true) + var card: Card? = null +} + @NoRepositoryBean interface GenericUserDataRepo : JpaRepository { fun findByCard(card: Card): T? diff --git a/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt b/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt index d1de2e28..740a5ea8 100644 --- a/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt +++ b/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt @@ -3,8 +3,6 @@ package icu.samnyan.aqua.sega.general.model import com.fasterxml.jackson.annotation.JsonIgnore import icu.samnyan.aqua.net.db.AquaNetUser import jakarta.persistence.* -import java.io.Serial -import java.io.Serializable import java.time.LocalDateTime /** @@ -43,12 +41,7 @@ class Card( // Whether the card is a ghost card @Column(name = "is_ghost") var isGhost: Boolean = false, -): Serializable { - companion object { - @Serial - private val serialVersionUID = 1L - } - +) { @Suppress("unused") // Used by serialization val isLinked get() = aquaUser != null } diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt new file mode 100644 index 00000000..919d50c5 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt @@ -0,0 +1,30 @@ +package icu.samnyan.aqua.sega.wacca.model.db + +import jakarta.transaction.Transactional +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.repository.NoRepositoryBean + +interface WcUserRepo : JpaRepository { + fun findByCardExtId(extId: Long): WaccaUser? +} + +@NoRepositoryBean +interface IWaccaUserLinked : JpaRepository { + fun findByUser(user: WaccaUser): List + fun findByUserCardExtId(userId: Long): List + @Transactional + fun deleteByUser(user: WaccaUser) +} + +interface WcUserOptionRepo : IWaccaUserLinked +interface WcUserBingoRepo : IWaccaUserLinked +interface WcUserFriendRepo : IWaccaUserLinked +interface WcUserFavoriteSongRepo : IWaccaUserLinked +interface WcUserGateRepo : IWaccaUserLinked +interface WcUserItemRepo : IWaccaUserLinked +interface WcUserTicketRepo : IWaccaUserLinked +interface WcUserSongUnlockRepo : IWaccaUserLinked +interface WcUserTrophyRepo : IWaccaUserLinked +interface WcUserBestScoreRepo : IWaccaUserLinked +interface WcUserPlayLogRepo : IWaccaUserLinked +interface WcUserStageUpRepo : IWaccaUserLinked \ No newline at end of file diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt new file mode 100644 index 00000000..b161b506 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt @@ -0,0 +1,57 @@ +package icu.samnyan.aqua.sega.wacca.model.db + +import icu.samnyan.aqua.net.games.BaseEntity +import icu.samnyan.aqua.sega.general.model.Card +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.JoinColumn +import jakarta.persistence.OneToOne +import jakarta.persistence.Table + +/** + * General user information + */ +@Entity @Table(name = "wacca_user") +class WaccaUser : BaseEntity() { + @OneToOne + @JoinColumn(name = "aime_card_id", unique = true) + var card: Card? = null + + @Column(length = 8) + var username = "" + + var xp = 0 + var wp = 0 + var wpTotal = 0 + var wpSpent = 0 + var danType = 0 + var danLevel = 0 + var title0 = 0 + var title1 = 0 + var title2 = 0 + var rating = 0 + var vipExpireTime: String? = null + var alwaysVip = false + var loginCount = 0 + var loginCountConsec = 0 + var loginCountDays = 0 + var loginCountDaysConsec = 0 + var loginCountToday = 0 + var playcountSingle = 0 + var playcountMultiVs = 0 + var playcountMultiCoop = 0 + var playcountStageup = 0 + var playcountTimeFree = 0 + var friendView1 = 0 + var friendView2 = 0 + var friendView3 = 0 + @Column(length = 50) + var lastGameVer = "" + var lastSongId = 0 + var lastSongDifficulty = 0 + var lastFolderOrder = 0 + var lastFolderId = 0 + var lastSongOrder = 0 + var lastLoginDate: String? = null + var gateTutorialFlags: String? = null +} \ No newline at end of file diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt new file mode 100644 index 00000000..ec110181 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt @@ -0,0 +1,147 @@ +package icu.samnyan.aqua.sega.wacca.model.db + +import com.fasterxml.jackson.annotation.JsonIgnore +import icu.samnyan.aqua.net.games.BaseEntity +import jakarta.persistence.* + +typealias UC = UniqueConstraint + +/** + * Base entity for all wacca user-related entities + */ +@MappedSuperclass +open class WaccaUserEntity : BaseEntity() { + @JsonIgnore + @ManyToOne + @JoinColumn(name = "user_id") + open var user: WaccaUser = WaccaUser() +} + +/** + * In-game option key-value storage + */ +@Entity @Table(name = "wacca_user_option", uniqueConstraints = [UC("", ["user_id", "opt_id"])]) +class WcUserOption : WaccaUserEntity() { + var optId = 0 + var value = 0 +} + +@Entity @Table(name = "wacca_user_bingo", uniqueConstraints = [UC("", ["user_id", "page_number"])]) +class WcUserBingo : WaccaUserEntity() { + var pageNumber = 0 + var pageProgress = "" +} + +/** + * The user here is the sender of the friend request. + */ +@Entity @Table(name = "wacca_friend", uniqueConstraints = [UC("", ["user_id", "with"])]) +class WcUserFriend : WaccaUserEntity() { + @ManyToOne @JoinColumn(name = "profile_reciever") + var with: WaccaUser = WaccaUser() + var isAccepted = false +} + +@Entity @Table(name = "wacca_user_favorite_song", uniqueConstraints = [UC("", ["user_id", "song_id"])]) +class WcUserFavoriteSong : WaccaUserEntity() { + // TODO: Make this into a list instead? + var songId = 0 +} + +@Entity @Table(name = "wacca_user_gate", uniqueConstraints = [UC("", ["user_id", "gate_id"])]) +class WcUserGate : WaccaUserEntity() { + var gateId = 0 + var page = 0 + var progress = 0 + var loops = 0 + var missionFlag = 0 + var totalPoints = 0 +} + +@Entity @Table(name = "wacca_user_item", uniqueConstraints = [UC("", ["user_id", "item_id", "type"])]) +class WcUserItem : WaccaUserEntity() { + var itemId = 0 + var type = 0 + var acquireDate = "" + var useCount = 0 +} + +@Entity @Table(name = "wacca_user_ticket", uniqueConstraints = [UC("", ["user_id", "ticket_id"])]) +class WcUserTicket : WaccaUserEntity() { + var ticketId = 0 + var acquireDate = "" + var expireDate = "" +} + +@Entity @Table(name = "wacca_user_song_unlock", uniqueConstraints = [UC("", ["user_id", "song_id"])]) +class WcUserSongUnlock : WaccaUserEntity() { + var songId = 0 + var highestDifficulty = 0 + var acquireDate = "" +} + +@Entity @Table(name = "wacca_user_trophy", uniqueConstraints = [UC("", ["user_id", "trophy_id", "season"])]) +class WcUserTrophy : WaccaUserEntity() { + var trophyId = 0 + var season = 0 + var progress = 0 + var badgeType = 0 +} + +@Entity @Table(name = "wacca_user_score", uniqueConstraints = [UC("", ["user_id", "song_id", "chart_id"])]) +class WcUserScore : WaccaUserEntity() { + var songId = 0 + var chartId = 0 + var score = 0 + var playCt = 0 + var clearCt = 0 + var misslessCt = 0 + var fullcomboCt = 0 + var allmarvCt = 0 + var gradeDCt = 0 + var gradeCCt = 0 + var gradeBCt = 0 + var gradeACt = 0 + var gradeAACt = 0 + var gradeAAACt = 0 + var gradeSCt = 0 + var gradeSSCt = 0 + var gradeSSSCt = 0 + var gradeMasterCt = 0 + var gradeSpCt = 0 + var gradeSspCt = 0 + var gradeSsspCt = 0 + var bestCombo = 0 + var lowestMissCt = 0 + var rating = 0 +} + +@Entity @Table(name = "wacca_user_playlog", uniqueConstraints = [UC("", ["user_id", "song_id", "chart_id", "date_scored"])]) +class WcUserPlayLog : WaccaUserEntity() { + var songId = 0 + var chartId = 0 + var score = 0 + var clear = 0 + var grade = 0 + var maxCombo = 0 + var marvCt = 0 + var greatCt = 0 + var goodCt = 0 + var missCt = 0 + var fastCt = 0 + var lateCt = 0 + var season = 0 + var dateScored = "" +} + +@Entity @Table(name = "wacca_user_stageup", uniqueConstraints = [UC("", ["user_id", "stage_id"])]) +class WcUserStageUp : WaccaUserEntity() { + var version = 0 + var stageId = 0 + var clearStatus = 0 + var clearSongCt = 0 + var song1Score = 0 + var song2Score = 0 + var song3Score = 0 + var playCt = 0 +} diff --git a/src/main/resources/db/migration/mariadb/V1000_11__wacca_init.sql b/src/main/resources/db/migration/mariadb/V1000_11__wacca_init.sql new file mode 100644 index 00000000..08f1bd13 --- /dev/null +++ b/src/main/resources/db/migration/mariadb/V1000_11__wacca_init.sql @@ -0,0 +1,211 @@ +CREATE TABLE wacca_user +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + aime_card_id BIGINT NOT NULL, + username VARCHAR(8) NOT NULL, + xp INT NOT NULL, + wp INT NOT NULL, + wp_total INT NOT NULL, + wp_spent INT NOT NULL, + dan_type INT NOT NULL, + dan_level INT NOT NULL, + title0 INT NOT NULL, + title1 INT NOT NULL, + title2 INT NOT NULL, + rating INT NOT NULL, + vip_expire_time VARCHAR(255) NULL, + always_vip BIT(1) NOT NULL, + login_count INT NOT NULL, + login_count_consec INT NOT NULL, + login_count_days INT NOT NULL, + login_count_days_consec INT NOT NULL, + login_count_today INT NOT NULL, + playcount_single INT NOT NULL, + playcount_multi_vs INT NOT NULL, + playcount_multi_coop INT NOT NULL, + playcount_stageup INT NOT NULL, + playcount_time_free INT NOT NULL, + friend_view1 INT NOT NULL, + friend_view2 INT NOT NULL, + friend_view3 INT NOT NULL, + last_game_ver VARCHAR(50) NULL, + last_song_id INT NOT NULL, + last_song_difficulty INT NOT NULL, + last_folder_order INT NOT NULL, + last_folder_id INT NOT NULL, + last_song_order INT NOT NULL, + last_login_date VARCHAR(255) NULL, + gate_tutorial_flags VARCHAR(255) NULL, + CONSTRAINT wacca_user_detail_unique UNIQUE (aime_card_id), + CONSTRAINT wacca_user_detail_fk FOREIGN KEY (aime_card_id) REFERENCES main.sega_card (id) +); + +CREATE TABLE wacca_user_bingo +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + page_number INT NOT NULL, + page_progress VARCHAR(255) NULL, + CONSTRAINT wacca_user_bingo_unique UNIQUE (user_id, page_number), + CONSTRAINT fku_wacca_user_bingo FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_favorite_song +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + song_id INT NOT NULL, + CONSTRAINT wacca_user_favorite_song_unique UNIQUE (user_id, song_id), + CONSTRAINT fku_wacca_user_favorite_song FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_friend +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + `with` BIGINT NOT NULL, + is_accepted BIT(1) NOT NULL, + CONSTRAINT wacca_friend_unique UNIQUE (user_id, `with`), + CONSTRAINT fku_wacca_friend FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT fku_wacca_friend_2 FOREIGN KEY (`with`) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_gate +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + gate_id INT NOT NULL, + page INT NOT NULL, + progress INT NOT NULL, + loops INT NOT NULL, + mission_flag INT NOT NULL, + total_points INT NOT NULL, + CONSTRAINT wacca_user_gate_unique UNIQUE (user_id, gate_id), + CONSTRAINT fku_wacca_user_gate FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_item +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + item_id INT NOT NULL, + type INT NOT NULL, + acquire_date VARCHAR(255) NULL, + use_count INT NOT NULL, + CONSTRAINT wacca_user_item_unique UNIQUE (user_id, item_id), + CONSTRAINT fku_wacca_user_item FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_score +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + song_id INT NOT NULL, + chart_id INT NOT NULL, + score INT NOT NULL, + play_ct INT NOT NULL, + clear_ct INT NOT NULL, + missless_ct INT NOT NULL, + fullcombo_ct INT NOT NULL, + allmarv_ct INT NOT NULL, + gradedct INT NOT NULL, + gradecct INT NOT NULL, + gradebct INT NOT NULL, + gradeact INT NOT NULL, + gradeaact INT NOT NULL, + gradeaaact INT NOT NULL, + gradesct INT NOT NULL, + gradessct INT NOT NULL, + gradesssct INT NOT NULL, + grade_master_ct INT NOT NULL, + grade_sp_ct INT NOT NULL, + grade_ssp_ct INT NOT NULL, + grade_sssp_ct INT NOT NULL, + best_combo INT NOT NULL, + lowest_miss_ct INT NOT NULL, + rating INT NOT NULL, + CONSTRAINT wacca_user_score_unique UNIQUE (user_id, song_id, chart_id), + CONSTRAINT fku_wacca_user_score FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_playlog +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + song_id INT NOT NULL, + chart_id INT NOT NULL, + score INT NOT NULL, + clear INT NOT NULL, + grade INT NOT NULL, + max_combo INT NOT NULL, + marv_ct INT NOT NULL, + great_ct INT NOT NULL, + good_ct INT NOT NULL, + miss_ct INT NOT NULL, + fast_ct INT NOT NULL, + late_ct INT NOT NULL, + season INT NOT NULL, + date_scored VARCHAR(255) NULL, + CONSTRAINT wacca_user_playlog_unique UNIQUE (user_id, song_id, chart_id), + CONSTRAINT fku_wacca_user_playlog FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_stageup +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + version INT NOT NULL, + stage_id INT NOT NULL, + clear_status INT NOT NULL, + clear_song_ct INT NOT NULL, + song1score INT NOT NULL, + song2score INT NOT NULL, + song3score INT NOT NULL, + play_ct INT NOT NULL, + CONSTRAINT wacca_user_stageup_unique UNIQUE (user_id, version, stage_id), + CONSTRAINT fku_wacca_user_stageup FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_song_unlock +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + song_id INT NOT NULL, + highest_difficulty INT NOT NULL, + acquire_date VARCHAR(255) NULL, + CONSTRAINT wacca_user_song_unlock_unique UNIQUE (user_id, song_id), + CONSTRAINT fku_wacca_user_song_unlock FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_ticket +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + ticket_id INT NOT NULL, + acquire_date VARCHAR(255) NULL, + expire_date VARCHAR(255) NULL, + CONSTRAINT wacca_user_ticket_unique UNIQUE (user_id, ticket_id), + CONSTRAINT fku_wacca_user_ticket FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_trophy +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + trophy_id INT NOT NULL, + season INT NOT NULL, + progress INT NOT NULL, + badge_type INT NOT NULL, + CONSTRAINT wacca_user_trophy_unique UNIQUE (user_id, trophy_id, season), + CONSTRAINT fku_wacca_user_trophy FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE wacca_user_option +( + id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, + user_id BIGINT NULL, + opt_id INT NOT NULL, + value INT NOT NULL, + CONSTRAINT wacca_user_option_unique UNIQUE (user_id, opt_id), + CONSTRAINT fku_wacca_user_option FOREIGN KEY (user_id) REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE +); \ No newline at end of file