From 04a178eda67c6378606ec6f865f44b623a774662 Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Fri, 27 Dec 2024 22:05:46 -0500 Subject: [PATCH] [+] Free paging --- .../samnyan/aqua/sega/chusan/ChusanApis.kt | 8 +--- .../sega/chusan/handler/GameLoginHandler.kt | 2 +- .../samnyan/aqua/sega/general/BaseHandler.kt | 47 +++++++++++++++++-- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt b/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt index bc8248c6..f81d4a89 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanApis.kt @@ -98,11 +98,7 @@ val chusanInit: ChusanController.() -> Unit = { mapOf("userId" to uid, "length" to lst.size, "userCardPrintStateList" to lst) } - ls("GetUserCharacter", "CMGetUserCharacter") all { - // Let's try not paging at all - val lst = db.userCharacter.findByUser_Card_ExtId(uid) - mapOf("userId" to uid, "length" to lst.size, "nextIndex" to -1, "userCharacterList" to lst) - } + "GetUserCharacter".paged("userCharacterList") { db.userCharacter.findByUser_Card_ExtId(uid) } "GetUserCourse" { val lst = db.userCourse.findByUser_Card_ExtId(uid) @@ -111,7 +107,7 @@ val chusanInit: ChusanController.() -> Unit = { } } - ls("GetUserItem", "CMGetUserItem") all { + "GetUserItem" { val kind = parsing { (data["nextIndex"]!!.long / 10000000000L).int } val maxCount = parsing { data["maxCount"]!!.int } // TODO pagination diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/handler/GameLoginHandler.kt b/src/main/java/icu/samnyan/aqua/sega/chusan/handler/GameLoginHandler.kt index d3c10065..a83a2723 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chusan/handler/GameLoginHandler.kt +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/handler/GameLoginHandler.kt @@ -22,7 +22,7 @@ class GameLoginHandler( val props: ChusanProps, val db: Chu3Repos ) : BaseHandler { - + override fun handle(request: Map): Any? { val uid = request["userId"]!!.long fun process() { diff --git a/src/main/java/icu/samnyan/aqua/sega/general/BaseHandler.kt b/src/main/java/icu/samnyan/aqua/sega/general/BaseHandler.kt index 233bff11..7b5fe1e3 100644 --- a/src/main/java/icu/samnyan/aqua/sega/general/BaseHandler.kt +++ b/src/main/java/icu/samnyan/aqua/sega/general/BaseHandler.kt @@ -1,9 +1,9 @@ package icu.samnyan.aqua.sega.general import com.fasterxml.jackson.core.JsonProcessingException -import ext.long -import ext.parsing +import ext.* import jakarta.servlet.http.HttpServletRequest +import org.springframework.scheduling.annotation.Scheduled fun interface BaseHandler { @@ -11,20 +11,59 @@ fun interface BaseHandler { fun handle(request: Map): Any? } -data class RequestContext( +class RequestContext( val req: HttpServletRequest, val data: Map, ) { val uid by lazy { parsing { data["userId"]!!.long } } + val nextIndex by lazy { parsing { data["nextIndex"]?.int ?: -1 } } + val maxCount by lazy { parsing { data["maxCount"]?.int ?: Int.MAX_VALUE } } } typealias SpecialHandler = RequestContext.() -> Any? fun BaseHandler.toSpecial() = { ctx: RequestContext -> handle(ctx.data) } +typealias PagedHandler = RequestContext.() -> List +typealias AddFn = RequestContext.() -> Map + // A very :3 way of declaring APIs abstract class MeowApi(val serialize: (String, Any?) -> String) { val initH = mutableMapOf() infix operator fun String.invoke(fn: SpecialHandler) = initH.set("${this}Api", fn) - infix fun List.all(fn: SpecialHandler) = forEach { it(fn) } infix fun String.static(fn: () -> Any) = serialize(this, fn()).let { resp -> this { resp } } + + // Page Cache: {cache key: (timestamp, full list)} + val pageCache = mutableMapOf>>() + + private fun String.pagedHelper(key: String, fn: PagedHandler, addFn: AddFn?) = this api@ { + val add = addFn?.invoke(this) ?: emptyMap() + + if (nextIndex == -1) return@api fn().let { + mapOf("userId" to uid, "length" to it.size, "nextIndex" to -1, key to it) + add + } + + // Try to get cache + val cacheKey = "$this:$uid:$add" + val list = pageCache.getOrPut(cacheKey) { millis() to fn() }.r + + // Get sublist and next index + val iAfter = (nextIndex + maxCount).coerceAtMost(list.size) + val lst = list.slice(nextIndex until iAfter) + + // Update cache if needed + if (iAfter == list.size) pageCache.remove(cacheKey) + else pageCache[cacheKey] = millis() to list + + mapOf("userId" to uid, "length" to lst.size, "nextIndex" to iAfter, key to lst) + add + } + fun String.paged(key: String, fn: PagedHandler) = pagedHelper(key, fn, null) + fun String.pagedWith(key: String, fn: PagedHandler) = this to key to fn + infix fun Pair>.with(addFn: AddFn) = l.pagedHelper(r.l, r.r, addFn) + + // Page cache cleanup every minute + @Scheduled(fixedRate = (1000 * 60)) + fun cleanupCache() { + val minTime = millis() - (1000 * 60) + pageCache.entries.removeIf { it.value.l < minTime } + } }