fix(tongji): 恢复游戏进度并完善周榜
This commit is contained in:
@@ -22,3 +22,5 @@ unpackage/dist
|
||||
*.sw?
|
||||
|
||||
TUICallKit
|
||||
# 本工程的通话源码复用仓库内 ../wx/TUICallKit;保留相对软链接以便干净克隆后可直接构建。
|
||||
!TUICallKit
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../wx/TUICallKit
|
||||
@@ -39,6 +39,7 @@ export function useGamePlatform(proxy, ensureLoggedIn) {
|
||||
const connected = ref(false)
|
||||
const loading = ref(false)
|
||||
const syncStatus = ref('idle')
|
||||
const lastError = ref('')
|
||||
const leaderboard = ref({ ...EMPTY_BOARD })
|
||||
const confirmedSessionLearned = ref(0)
|
||||
|
||||
@@ -73,17 +74,19 @@ export function useGamePlatform(proxy, ensureLoggedIn) {
|
||||
}
|
||||
connected.value = true
|
||||
syncStatus.value = 'synced'
|
||||
lastError.value = ''
|
||||
}
|
||||
|
||||
async function refreshLeaderboard() {
|
||||
const res = await api({
|
||||
url: '/api/tcm/gameWeeklyLeaderboard',
|
||||
method: 'GET'
|
||||
method: 'GET',
|
||||
data: { session_key: sessionKey }
|
||||
})
|
||||
if (res?.code !== 1 || !res.data) {
|
||||
throw new Error(res?.msg || '同行榜加载失败')
|
||||
}
|
||||
applyLeaderboard(res.data)
|
||||
applyLeaderboard(res.data, sessionKey)
|
||||
return res.data
|
||||
}
|
||||
|
||||
@@ -112,9 +115,10 @@ export function useGamePlatform(proxy, ensureLoggedIn) {
|
||||
await refreshLeaderboard()
|
||||
if (queuedPayloads.length) flushProgress()
|
||||
return true
|
||||
} catch (_) {
|
||||
} catch (error) {
|
||||
connected.value = false
|
||||
syncStatus.value = 'offline'
|
||||
lastError.value = String(error?.message || '平台连接失败,请点击重试')
|
||||
return false
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -124,14 +128,21 @@ export function useGamePlatform(proxy, ensureLoggedIn) {
|
||||
return connectPromise
|
||||
}
|
||||
|
||||
function beginSession() {
|
||||
function beginSession(existingSessionKey = '') {
|
||||
if (syncTimer) clearTimeout(syncTimer)
|
||||
sessionKey = createSessionKey()
|
||||
const restoredKey = String(existingSessionKey || '').trim()
|
||||
sessionKey = /^[A-Za-z0-9_-]{16,64}$/.test(restoredKey)
|
||||
? restoredKey
|
||||
: createSessionKey()
|
||||
confirmedSessionLearned.value = 0
|
||||
syncStatus.value = queuedPayloads.length ? 'pending' : (connected.value ? 'synced' : 'offline')
|
||||
return sessionKey
|
||||
}
|
||||
|
||||
function getSessionKey() {
|
||||
return sessionKey
|
||||
}
|
||||
|
||||
function queueProgress(learnedCount, score, ended = false) {
|
||||
const nextPayload = {
|
||||
session_key: sessionKey,
|
||||
@@ -191,9 +202,10 @@ export function useGamePlatform(proxy, ensureLoggedIn) {
|
||||
|| Number(item.ended) > Number(payload.ended)
|
||||
))
|
||||
persistPendingPayloads()
|
||||
} catch (_) {
|
||||
} catch (error) {
|
||||
connected.value = false
|
||||
syncStatus.value = 'offline'
|
||||
lastError.value = String(error?.message || '成绩暂未保存,联网后会自动重试')
|
||||
// 队首保留原绝对值,下次连接或打开榜单时安全重试。
|
||||
persistPendingPayloads()
|
||||
} finally {
|
||||
@@ -237,10 +249,12 @@ export function useGamePlatform(proxy, ensureLoggedIn) {
|
||||
connected,
|
||||
loading,
|
||||
syncStatus,
|
||||
lastError,
|
||||
leaderboard,
|
||||
confirmedSessionLearned,
|
||||
connect,
|
||||
beginSession,
|
||||
getSessionKey,
|
||||
queueProgress,
|
||||
flushProgress,
|
||||
syncAndRefresh,
|
||||
|
||||
@@ -337,6 +337,9 @@
|
||||
.eg-weekly-list { display: flex; flex-direction: column; gap: 7rpx; margin-top: 15rpx; }
|
||||
.eg-weekly-row { display: flex; min-height: 68rpx; align-items: center; padding: 7rpx 14rpx; border: 2rpx solid transparent; border-radius: 19rpx; color: #345c50; background: rgba(255,255,255,.72); }
|
||||
.eg-weekly-row.is-me { border-color: #f2bd48; color: #174f3e; background: linear-gradient(90deg, #fff2bb, #fff9e3); box-shadow: 0 5rpx 14rpx rgba(153,102,17,.12); transform: scale(1.015); }
|
||||
.eg-weekly-row.is-waiting { color: #8a9b94; border-style: dashed; border-color: #dbe8e2; background: rgba(247,251,249,.72); }
|
||||
.eg-weekly-row.is-waiting .eg-weekly-avatar { color: #6e887e; border-color: #dce9e3; background: #edf4f1; box-shadow: none; }
|
||||
.eg-weekly-row.is-waiting .eg-weekly-count { color: #9baba4; }
|
||||
.eg-weekly-place { width: 43rpx; color: #6b7d76; font-size: 27rpx; font-weight: 1000; text-align: center; }
|
||||
.eg-weekly-row:nth-child(1) .eg-weekly-place { color: #dd7b17; font-size: 31rpx; }
|
||||
.eg-weekly-avatar { display: flex; width: 52rpx; height: 52rpx; flex: 0 0 52rpx; align-items: center; justify-content: center; margin-left: 6rpx; border: 3rpx solid rgba(255,255,255,.9); border-radius: 50%; color: #fff; background: #5d9b84; box-shadow: 0 3rpx 9rpx rgba(30,78,61,.16); font-size: 24rpx; font-weight: 1000; }
|
||||
@@ -350,6 +353,9 @@
|
||||
.eg-weekly-name { flex: 1; margin-left: 13rpx; overflow: hidden; font-size: 27rpx; font-weight: 850; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.eg-weekly-me { margin-right: 8rpx; padding: 3rpx 9rpx; border-radius: 999rpx; color: #fff; background: #dd6b27; font-size: 18rpx; font-weight: 900; }
|
||||
.eg-weekly-count { min-width: 70rpx; color: #315b4d; font-size: 29rpx; font-weight: 1000; text-align: right; }
|
||||
.eg-weekly-connection { display: flex; align-items: center; justify-content: space-between; gap: 12rpx; margin-top: 13rpx; padding: 12rpx 15rpx; border: 2rpx solid #e4d7b2; border-radius: 18rpx; color: #6b6655; background: #fff9e8; font-size: 21rpx; font-weight: 700; }
|
||||
.eg-weekly-connection > text:first-child { flex: 1; }
|
||||
.eg-weekly-retry { flex-shrink: 0; padding: 6rpx 12rpx; border-radius: 999rpx; color: #fff; background: #23765c; font-weight: 900; }
|
||||
.eg-cheer-card { margin-top: 16rpx; padding: 15rpx 17rpx 14rpx; border: 3rpx solid #f0d38d; border-radius: 23rpx; background: linear-gradient(135deg, #fff9d9, #fff1c2); }
|
||||
.eg-cheer-card.is-family { border-color: #d9c8ef; background: linear-gradient(135deg, #f8f2ff, #efe7ff); }
|
||||
.eg-cheer-card.is-bright { border-color: #f4c77b; background: linear-gradient(135deg, #fff8dc, #ffeec7); }
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
v-for="player in weeklyRankRows"
|
||||
:key="player.id"
|
||||
class="eg-weekly-row"
|
||||
:class="{ 'is-me': player.isMe }"
|
||||
:class="{ 'is-me': player.isMe, 'is-waiting': player.isWaiting }"
|
||||
>
|
||||
<text class="eg-weekly-place">{{ player.rank }}</text>
|
||||
<view class="eg-weekly-avatar" :class="`is-tone-${player.tone}`">
|
||||
@@ -237,6 +237,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!platformConnected" class="eg-weekly-connection" @tap.stop="retryPlatformConnection">
|
||||
<text>{{ platformConnectionHint }}</text>
|
||||
<text class="eg-weekly-retry">点击重试</text>
|
||||
</view>
|
||||
|
||||
<view class="eg-cheer-card" :class="`is-${currentCheer.tone}`">
|
||||
<view class="eg-cheer-top">
|
||||
<text class="eg-cheer-source">{{ currentCheer.source }}</text>
|
||||
@@ -246,7 +251,7 @@
|
||||
<text v-if="currentCheer.userGenerated" class="eg-cheer-note">亲友自定义内容,不作为医学判断</text>
|
||||
</view>
|
||||
|
||||
<view class="eg-weekly-start" @tap="closeWeeklyRank">开始消除</view>
|
||||
<view class="eg-weekly-start" @tap="closeWeeklyRank">{{ restoredGame ? '继续上次游戏' : '开始消除' }}</view>
|
||||
<button class="eg-weekly-share" open-type="share">邀请亲友一起认糖</button>
|
||||
<text class="eg-weekly-footnote">使用平台昵称参加真实周榜,每周一重新分组;分享不会自动绑定家庭关系</text>
|
||||
</view>
|
||||
@@ -300,7 +305,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import { onLoad, onShareAppMessage, onShareTimeline, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import { onHide, onLoad, onShareAppMessage, onShareTimeline, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import TongjiIcon from '../components/TongjiIcon.vue'
|
||||
import { useGameSfx } from './composables/useGameSfx.js'
|
||||
import { useGamePlatform } from './composables/useGamePlatform.js'
|
||||
@@ -319,6 +324,8 @@ const COMBO_TASK_WAVE = 2
|
||||
const COMBO_TASK_TARGET = 2
|
||||
const COMBO_PITY_TURNS = 6
|
||||
const MEGA_COMBO_SCORE = 300
|
||||
const ACTIVE_GAME_KEY = 'tongji_endless_active_game_v1'
|
||||
const ACTIVE_GAME_VERSION = 1
|
||||
const DANGER_DROP_CHANCE = 0.05 + GAME_DIFFICULTY * 0.01
|
||||
const COLUMN_GROUP_BIAS = 0.86 - GAME_DIFFICULTY * 0.028
|
||||
const CHEER_MESSAGES = [
|
||||
@@ -399,10 +406,12 @@ const {
|
||||
connected: platformConnected,
|
||||
loading: platformLoading,
|
||||
syncStatus: platformSyncStatus,
|
||||
lastError: platformLastError,
|
||||
leaderboard: platformLeaderboard,
|
||||
confirmedSessionLearned,
|
||||
connect: connectPlatform,
|
||||
beginSession: beginPlatformSession,
|
||||
getSessionKey: getPlatformSessionKey,
|
||||
queueProgress: queuePlatformProgress,
|
||||
flushProgress: flushPlatformProgress,
|
||||
syncAndRefresh: syncAndRefreshPlatform,
|
||||
@@ -447,6 +456,7 @@ const reshuffleDone = ref(false)
|
||||
const reshuffling = ref(false)
|
||||
const confettiVisible = ref(false)
|
||||
const weeklyRankVisible = ref(false)
|
||||
const restoredGame = ref(false)
|
||||
const cheerIndex = ref(0)
|
||||
const CONFETTI_COLORS = ['#ff3154', '#ffd928', '#28c76f', '#4d8dff', '#a855f7', '#ff7a1a']
|
||||
const confettiPieces = Array.from({ length: 34 }, (_, index) => ({
|
||||
@@ -515,9 +525,24 @@ const weeklyRankRows = computed(() => {
|
||||
tone: 1,
|
||||
isMe: true
|
||||
}]
|
||||
return source
|
||||
const ranked = source
|
||||
.sort((a, b) => b.count - a.count || Number(b.best_score || 0) - Number(a.best_score || 0))
|
||||
.map((player, index) => ({ ...player, rank: index + 1 }))
|
||||
while (ranked.length < 7) {
|
||||
const rank = ranked.length + 1
|
||||
ranked.push({
|
||||
id: `waiting-${rank}`,
|
||||
rank,
|
||||
name: '等待同行加入',
|
||||
avatar: '+',
|
||||
avatarUrl: '',
|
||||
count: '—',
|
||||
tone: rank,
|
||||
isMe: false,
|
||||
isWaiting: true
|
||||
})
|
||||
}
|
||||
return ranked.slice(0, 7)
|
||||
})
|
||||
const weeklyMyRank = computed(() => weeklyRankRows.value.find(player => player.isMe)?.rank || 1)
|
||||
const weeklyRankRise = computed(() => 0)
|
||||
@@ -535,13 +560,24 @@ const weeklyRangeLabel = computed(() => {
|
||||
return `${formatWeekDate(start)}—${formatWeekDate(end)}`
|
||||
})
|
||||
const weeklySexLabel = computed(() => platformLeaderboard.value.sex_label || '同行')
|
||||
const weeklyMemberCount = computed(() => Math.max(1, Number(platformLeaderboard.value.member_count || weeklyRankRows.value.length)))
|
||||
const weeklyMemberCount = computed(() => {
|
||||
const reported = Number(platformLeaderboard.value.member_count || 0)
|
||||
const remoteCount = Array.isArray(platformLeaderboard.value.players)
|
||||
? platformLeaderboard.value.players.length
|
||||
: 0
|
||||
return Math.max(1, Math.min(7, reported || remoteCount || 1))
|
||||
})
|
||||
const weeklyStatusLabel = computed(() => {
|
||||
if (platformLoading.value) return '连接中'
|
||||
if (!platformConnected.value) return '离线记录中'
|
||||
if (platformSyncStatus.value === 'pending' || platformSyncStatus.value === 'syncing') return '保存中'
|
||||
return '真实周榜'
|
||||
})
|
||||
const platformConnectionHint = computed(() => (
|
||||
platformLoading.value
|
||||
? '正在连接甄养堂账号和本周同行榜'
|
||||
: (platformLastError.value || '当前成绩暂存在本机,联网登录后会自动补传')
|
||||
))
|
||||
|
||||
onShareAppMessage(() => {
|
||||
recordPlatformShare()
|
||||
@@ -592,7 +628,7 @@ onLoad((options = {}) => {
|
||||
initNavLayout()
|
||||
bestScore.value = Number(uni.getStorageSync('tongji_endless_best') || 0)
|
||||
incomingInviteCode = String(options.invite_code || '').trim()
|
||||
startGame()
|
||||
if (!restoreActiveGame()) startGame()
|
||||
weeklyRankVisible.value = true
|
||||
connectPlatform(incomingInviteCode)
|
||||
})
|
||||
@@ -603,7 +639,12 @@ onShow(() => {
|
||||
})
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
saveActiveGame()
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
saveActiveGame()
|
||||
if (learned.value > 0 || gameOver.value) {
|
||||
queuePlatformProgress(learned.value, score.value, gameOver.value)
|
||||
flushPlatformProgress()
|
||||
@@ -679,8 +720,117 @@ function cycleCheer() {
|
||||
playSfx('select', .68)
|
||||
}
|
||||
|
||||
async function retryPlatformConnection() {
|
||||
if (platformLoading.value) return
|
||||
const connected = await connectPlatform(incomingInviteCode)
|
||||
if (connected) {
|
||||
await syncAndRefreshPlatform(learned.value, score.value)
|
||||
uni.showToast({ title: '周榜已更新', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: platformConnectionHint.value, icon: 'none' })
|
||||
}
|
||||
|
||||
function currentWeekStartKey(now = new Date()) {
|
||||
const date = new Date(now)
|
||||
const day = date.getDay() || 7
|
||||
date.setHours(0, 0, 0, 0)
|
||||
date.setDate(date.getDate() - day + 1)
|
||||
const pad = value => String(value).padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
|
||||
}
|
||||
|
||||
function clearActiveGame() {
|
||||
try { uni.removeStorageSync(ACTIVE_GAME_KEY) } catch (_) {}
|
||||
}
|
||||
|
||||
function saveActiveGame() {
|
||||
if (gameOver.value) {
|
||||
clearActiveGame()
|
||||
return false
|
||||
}
|
||||
if (busy.value || board.value.length !== ROWS * COLS) return false
|
||||
const stableBoard = board.value.every(tile => tile && FOODS[tile.key])
|
||||
if (!stableBoard) return false
|
||||
try {
|
||||
uni.setStorageSync(ACTIVE_GAME_KEY, {
|
||||
version: ACTIVE_GAME_VERSION,
|
||||
week_start: currentWeekStartKey(),
|
||||
session_key: getPlatformSessionKey(),
|
||||
confirmed_session_learned: Math.max(0, Number(confirmedSessionLearned.value) || 0),
|
||||
saved_at: Date.now(),
|
||||
theme_index: themeIndex.value,
|
||||
board: board.value.map(tile => ({ id: tile.id, key: tile.key, special: tile.special || '' })),
|
||||
score: score.value,
|
||||
learned: learned.value,
|
||||
combo: combo.value,
|
||||
feast_moves: feastMoves.value,
|
||||
camel_count: camelCount.value,
|
||||
task_index: taskIndex.value,
|
||||
task_progress: taskProgress.value,
|
||||
combo_dry_turns: comboDryTurns.value
|
||||
})
|
||||
return true
|
||||
} catch (_) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function restoreActiveGame() {
|
||||
let saved
|
||||
try { saved = uni.getStorageSync(ACTIVE_GAME_KEY) } catch (_) { return false }
|
||||
const validBoard = Array.isArray(saved?.board)
|
||||
&& saved.board.length === ROWS * COLS
|
||||
&& saved.board.every(tile => tile && FOODS[tile.key])
|
||||
if (saved?.version !== ACTIVE_GAME_VERSION || saved?.week_start !== currentWeekStartKey() || !validBoard) {
|
||||
clearActiveGame()
|
||||
return false
|
||||
}
|
||||
|
||||
themeIndex.value = Math.max(0, Math.min(FOOD_GROUPS.length - 1, Number(saved.theme_index) || 0))
|
||||
board.value = saved.board.map(tile => ({
|
||||
id: Math.max(1, Number(tile.id) || tileId++),
|
||||
key: tile.key,
|
||||
special: ['row', 'col', 'all', 'burst'].includes(tile.special) ? tile.special : ''
|
||||
}))
|
||||
tileId = Math.max(tileId, ...board.value.map(tile => tile.id + 1))
|
||||
score.value = Math.max(0, Number(saved.score) || 0)
|
||||
learned.value = Math.max(0, Number(saved.learned) || 0)
|
||||
combo.value = Math.max(0, Number(saved.combo) || 0)
|
||||
feastMoves.value = Math.max(0, Number(saved.feast_moves) || 0)
|
||||
camelCount.value = Math.max(0, Math.min(MAX_CAMEL_COUNT, Number(saved.camel_count) || 0))
|
||||
taskIndex.value = Math.max(0, Number(saved.task_index) || 0) % 4
|
||||
taskProgress.value = Math.max(0, Number(saved.task_progress) || 0)
|
||||
comboDryTurns.value = Math.max(0, Number(saved.combo_dry_turns) || 0)
|
||||
beginPlatformSession(saved.session_key)
|
||||
confirmedSessionLearned.value = Math.max(0, Number(saved.confirmed_session_learned) || 0)
|
||||
selected.value = -1
|
||||
camelMode.value = false
|
||||
busy.value = false
|
||||
gameOver.value = false
|
||||
taskRewardVisible.value = false
|
||||
clearingIndices.value = []
|
||||
fireClearingIndices.value = []
|
||||
impactText.value = ''
|
||||
camelJackpot.value.visible = false
|
||||
stageVisible.value = false
|
||||
reshuffleVisible.value = false
|
||||
reshuffleDone.value = false
|
||||
reshuffling.value = false
|
||||
confettiVisible.value = false
|
||||
pendingThemeMatchCount = 0
|
||||
taskRewardStatusUntil = 0
|
||||
endedFood.value = foodOf(currentTheme.value.danger)
|
||||
restoredGame.value = true
|
||||
resetDrag()
|
||||
statusText.value = '已恢复上次未完成的游戏和得分'
|
||||
scheduleHint()
|
||||
return true
|
||||
}
|
||||
|
||||
function startGame() {
|
||||
if (hintTimer) clearTimeout(hintTimer)
|
||||
clearActiveGame()
|
||||
beginPlatformSession()
|
||||
themeIndex.value = 0
|
||||
board.value = buildPlayableBoard(openingKeysForTheme(currentTheme.value))
|
||||
@@ -708,8 +858,10 @@ function startGame() {
|
||||
confettiVisible.value = false
|
||||
pendingThemeMatchCount = 0
|
||||
taskRewardStatusUntil = 0
|
||||
restoredGame.value = false
|
||||
resetDrag()
|
||||
statusText.value = '交换相邻食物,三个相同即可消除'
|
||||
saveActiveGame()
|
||||
scheduleHint()
|
||||
}
|
||||
|
||||
@@ -863,6 +1015,7 @@ async function tapTile(index) {
|
||||
finishTurn()
|
||||
} else {
|
||||
statusText.value = `已将${foodOf(currentTheme.value.danger).short}移出餐盘,凑齐三个驼乳可换主题`
|
||||
saveActiveGame()
|
||||
if (!checkBoardHealth()) scheduleHint()
|
||||
}
|
||||
return
|
||||
@@ -928,6 +1081,7 @@ async function trySwap(a, b, soundPlayed = false) {
|
||||
endedFood.value = foodOf(highTile.key)
|
||||
saveBest()
|
||||
gameOver.value = true
|
||||
clearActiveGame()
|
||||
queuePlatformProgress(learned.value, score.value, true)
|
||||
busy.value = false
|
||||
return
|
||||
@@ -1422,6 +1576,7 @@ function showTaskCamelReward() {
|
||||
|
||||
function finishTurn() {
|
||||
saveBest()
|
||||
saveActiveGame()
|
||||
if (checkBoardHealth()) return
|
||||
if (stageVisible.value) {
|
||||
scheduleHint()
|
||||
@@ -1497,6 +1652,7 @@ async function performReshuffle() {
|
||||
reshuffleVisible.value = false
|
||||
reshuffling.value = false
|
||||
busy.value = false
|
||||
saveActiveGame()
|
||||
scheduleHint()
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ class TcmController extends BaseApiController
|
||||
/** 获取当前登录用户的控糖消消乐每周7人同行榜。 */
|
||||
public function gameWeeklyLeaderboard()
|
||||
{
|
||||
$result = GamePlatformLogic::leaderboard((int) $this->userId);
|
||||
$result = GamePlatformLogic::leaderboard(
|
||||
(int) $this->userId,
|
||||
(string) $this->request->get('session_key', '')
|
||||
);
|
||||
if ($result === false) {
|
||||
return $this->fail(GamePlatformLogic::getError());
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class GamePlatformLogic
|
||||
/**
|
||||
* 获取当前用户所在的真实周榜;首次进入会分配到当周同性别7人组。
|
||||
*/
|
||||
public static function leaderboard(int $userId): array|false
|
||||
public static function leaderboard(int $userId, string $sessionKey = ''): array|false
|
||||
{
|
||||
self::$error = '';
|
||||
if ($userId <= 0) {
|
||||
@@ -43,7 +43,16 @@ class GamePlatformLogic
|
||||
$score = self::ensureWeeklyScore($userId, self::weekStart());
|
||||
$inviteCode = self::ensureShareInvite($userId, self::weekStart());
|
||||
Db::commit();
|
||||
return self::buildLeaderboard((int) $score['group_id'], $userId, $inviteCode);
|
||||
$result = self::buildLeaderboard((int) $score['group_id'], $userId, $inviteCode);
|
||||
$sessionKey = trim($sessionKey);
|
||||
if (preg_match('/^[A-Za-z0-9_-]{16,64}$/', $sessionKey)) {
|
||||
$confirmed = Db::name('tcm_game_session')
|
||||
->where('session_key', $sessionKey)
|
||||
->where('user_id', $userId)
|
||||
->value('learned_count');
|
||||
$result['confirmed_session_learned'] = max(0, (int) $confirmed);
|
||||
}
|
||||
return $result;
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
self::logException('leaderboard', $e);
|
||||
|
||||
Reference in New Issue
Block a user