[+] Mai2 music ranking

pull/106/head
Menci 2025-01-12 03:53:57 +08:00
parent 65cc3095e2
commit 3d66b7e022
5 changed files with 106 additions and 1 deletions

View File

@ -13,6 +13,7 @@ plugins {
kotlin("plugin.jpa") version ktVer
kotlin("plugin.serialization") version ktVer
kotlin("plugin.allopen") version ktVer
kotlin("kapt") version ktVer
id("io.freefair.lombok") version "8.6"
id("org.springframework.boot") version "3.2.3"
id("com.github.ben-manes.versions") version "0.51.0"
@ -55,6 +56,8 @@ dependencies {
runtimeOnly("org.xerial:sqlite-jdbc:3.45.2.0")
implementation("org.hibernate.orm:hibernate-core:6.4.4.Final")
implementation("org.hibernate.orm:hibernate-community-dialects:6.4.4.Final")
implementation("io.github.openfeign.querydsl:querydsl-jpa:6.10.1")
kapt("io.github.openfeign.querydsl:querydsl-apt:6.10.1:jpa")
// JSR305 for nullable
implementation("com.google.code.findbugs:jsr305:3.0.2")
@ -122,6 +125,11 @@ hibernate {
}
}
kapt {
includeCompileClasspath = false
keepJavacAnnotationProcessors = true
}
allOpen {
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
@ -153,3 +161,9 @@ tasks.withType<Javadoc> {
tasks.getByName<Jar>("jar") {
enabled = false
}
sourceSets {
main {
java.srcDir("${layout.buildDirectory.get()}/generated/source/kapt/main")
}
}

View File

@ -33,6 +33,7 @@ class Maimai2ServletController(
val getUserFavoriteItem: GetUserFavoriteItemHandler,
val getUserRivalMusic: GetUserRivalMusicHandler,
val getUserCharacter: GetUserCharacterHandler,
val getGameRanking: GetGameRankingHandler,
val repos: Mai2Repos
) {
companion object {
@ -222,7 +223,6 @@ class Maimai2ServletController(
val getUserIntimate = UserReqHandler { _, uid -> mapOf("userId" to uid, "length" to 0, "userIntimateList" to empty) }
val getTransferFriend = UserReqHandler { _, uid -> mapOf("userId" to uid, "transferFriendList" to empty) }
val getGameNgMusicId = BaseHandler { mapOf("length" to 0, "musicIdList" to empty) }
val getGameRanking = BaseHandler { mapOf("type" to it["type"].toString(), "gameRankingList" to empty) }
val getGameTournamentInfo = BaseHandler { mapOf("length" to 0, "gameTournamentInfoList" to empty) }
val getGameKaleidxScope = BaseHandler { mapOf("gameKaleidxScopeList" to empty) }
val getUserKaleidxScope = UserReqHandler { _, uid -> mapOf("userId" to uid, "userKaleidxScopeList" to empty) }

View File

@ -0,0 +1,71 @@
package icu.samnyan.aqua.sega.maimai2.handler
import com.querydsl.jpa.impl.JPAQueryFactory
import icu.samnyan.aqua.sega.general.BaseHandler
import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos
import icu.samnyan.aqua.sega.maimai2.model.userdata.QMai2UserPlaylog
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Component("Maimai2GetGameRankingHandler")
class GetGameRankingHandler(
private val repos: Mai2Repos,
private val queryFactory: JPAQueryFactory
) : BaseHandler {
private data class MusicRankingItem(val musicId: Int, val weight: Long)
private var musicRankingCache: Array<MusicRankingItem> = emptyArray()
init {
// To make sure the cache is initialized before the first request,
// not using `initialDelay = 0` in `@Scheduled`.
refreshMusicRankingCache()
}
@Scheduled(fixedDelay = 3600_000)
private fun refreshMusicRankingCache() {
val LOOK_BACK_DAYS: Long = 7
val QUREY_LIMIT: Long = 50
val qMai2Playlog = QMai2UserPlaylog.mai2UserPlaylog
val qPlaylog = QMai2UserPlaylog.mai2UserPlaylog
// Get the play count of each music in the last N days
val queryAfter = LocalDateTime.now().minusDays(LOOK_BACK_DAYS)
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val queryAfterStr = queryAfter.format(formatter)
val cMusicId = qPlaylog.musicId
val cUserCount = qPlaylog.user.id.countDistinct()
musicRankingCache = queryFactory
.select(cMusicId, cUserCount)
.from(qPlaylog)
.where(qPlaylog.userPlayDate.stringValue().goe(queryAfterStr))
.groupBy(cMusicId)
.orderBy(cUserCount.desc())
.limit(QUREY_LIMIT)
.fetch()
.map { MusicRankingItem(it.get(cMusicId)!!, it.get(cUserCount)!!) }
.toTypedArray()
logger.info("Refreshed music ranking cache: ${musicRankingCache.size} items")
}
override fun handle(request: Map<String, Any>): Any = mapOf(
"type" to request["type"],
"gameRankingList" to when(request["type"]) {
1 -> musicRankingCache.map { mapOf("id" to it.musicId, "point" to it.weight, "userName" to "") }
else -> emptyList()
}
)
companion object {
val logger: Logger = LoggerFactory.getLogger(GetGameRankingHandler::class.java)
}
}

View File

@ -0,0 +1,18 @@
package icu.samnyan.aqua.spring
import com.querydsl.jpa.impl.JPAQueryFactory
import jakarta.persistence.EntityManager
import jakarta.persistence.PersistenceContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class QuerydslConfig {
@PersistenceContext
private lateinit var entityManager: EntityManager
@Bean
fun jpaQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(entityManager)
}
}

View File

@ -0,0 +1,2 @@
CREATE INDEX idx_play_date_music_user
ON maimai2_user_playlog (user_play_date, music_id, user_id);