릴리스: v0.1.43 토스트와 즐겨찾기 추가
This commit is contained in:
@@ -212,6 +212,18 @@ async function ensureSchema() {
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
`)
|
||||
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS favorite_tierlists (
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
tierlist_id VARCHAR(64) NOT NULL,
|
||||
created_at BIGINT NOT NULL,
|
||||
PRIMARY KEY (user_id, tierlist_id),
|
||||
INDEX idx_favorite_tierlists_tierlist_id (tierlist_id),
|
||||
CONSTRAINT fk_favorite_tierlists_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_favorite_tierlists_tierlist FOREIGN KEY (tierlist_id) REFERENCES tierlists(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
`)
|
||||
|
||||
const tierListThumbnailColumns = await query("SHOW COLUMNS FROM tierlists LIKE 'thumbnail_src'")
|
||||
if (!tierListThumbnailColumns.length) {
|
||||
await query("ALTER TABLE tierlists ADD COLUMN thumbnail_src VARCHAR(255) NOT NULL DEFAULT '' AFTER title")
|
||||
@@ -610,7 +622,51 @@ async function findUnusedCustomItems({ queryText = '' } = {}) {
|
||||
.filter((item) => item.usageCount === 0)
|
||||
}
|
||||
|
||||
async function listPublicTierLists(gameId) {
|
||||
async function getFavoriteStatsForTierListIds(tierListIds, userId = '') {
|
||||
const ids = Array.from(new Set((tierListIds || []).filter(Boolean)))
|
||||
const countMap = new Map()
|
||||
const favoritedSet = new Set()
|
||||
if (!ids.length) return { countMap, favoritedSet }
|
||||
|
||||
const placeholders = ids.map(() => '?').join(', ')
|
||||
const countRows = await query(
|
||||
`
|
||||
SELECT tierlist_id, COUNT(*) AS favorite_count
|
||||
FROM favorite_tierlists
|
||||
WHERE tierlist_id IN (${placeholders})
|
||||
GROUP BY tierlist_id
|
||||
`,
|
||||
ids
|
||||
)
|
||||
|
||||
countRows.forEach((row) => {
|
||||
countMap.set(row.tierlist_id, Number(row.favorite_count || 0))
|
||||
})
|
||||
|
||||
if (userId) {
|
||||
const favoriteRows = await query(
|
||||
`
|
||||
SELECT tierlist_id
|
||||
FROM favorite_tierlists
|
||||
WHERE user_id = ? AND tierlist_id IN (${placeholders})
|
||||
`,
|
||||
[userId, ...ids]
|
||||
)
|
||||
favoriteRows.forEach((row) => favoritedSet.add(row.tierlist_id))
|
||||
}
|
||||
|
||||
return { countMap, favoritedSet }
|
||||
}
|
||||
|
||||
function applyFavoriteMetaToTierLists(tierLists, favoriteStats) {
|
||||
return tierLists.map((tierList) => ({
|
||||
...tierList,
|
||||
favoriteCount: favoriteStats.countMap.get(tierList.id) || 0,
|
||||
isFavorited: favoriteStats.favoritedSet.has(tierList.id),
|
||||
}))
|
||||
}
|
||||
|
||||
async function listPublicTierLists(gameId, currentUserId = '') {
|
||||
const params = []
|
||||
let whereClause = 'WHERE t.is_public = 1'
|
||||
if (gameId) {
|
||||
@@ -640,7 +696,7 @@ async function listPublicTierLists(gameId) {
|
||||
params
|
||||
)
|
||||
|
||||
return rows.map((row) => ({
|
||||
const tierLists = rows.map((row) => ({
|
||||
id: row.id,
|
||||
gameId: row.game_id,
|
||||
title: row.title,
|
||||
@@ -652,6 +708,12 @@ async function listPublicTierLists(gameId) {
|
||||
authorAccountName: getUserAccountName(row),
|
||||
authorAvatarSrc: row.avatar_src || '',
|
||||
}))
|
||||
|
||||
const favoriteStats = await getFavoriteStatsForTierListIds(
|
||||
tierLists.map((tierList) => tierList.id),
|
||||
currentUserId
|
||||
)
|
||||
return applyFavoriteMetaToTierLists(tierLists, favoriteStats)
|
||||
}
|
||||
|
||||
async function listUserTierLists(userId) {
|
||||
@@ -676,7 +738,7 @@ async function listUserTierLists(userId) {
|
||||
[userId]
|
||||
)
|
||||
|
||||
return rows.map((row) => ({
|
||||
const tierLists = rows.map((row) => ({
|
||||
id: row.id,
|
||||
gameId: row.game_id,
|
||||
title: row.title,
|
||||
@@ -688,6 +750,12 @@ async function listUserTierLists(userId) {
|
||||
authorAccountName: getUserAccountName(row),
|
||||
authorAvatarSrc: row.avatar_src || '',
|
||||
}))
|
||||
|
||||
const favoriteStats = await getFavoriteStatsForTierListIds(
|
||||
tierLists.map((tierList) => tierList.id),
|
||||
userId
|
||||
)
|
||||
return applyFavoriteMetaToTierLists(tierLists, favoriteStats)
|
||||
}
|
||||
|
||||
function uniqueTierListItems(poolItems) {
|
||||
@@ -704,7 +772,7 @@ function uniqueTierListItems(poolItems) {
|
||||
return Array.from(map.values())
|
||||
}
|
||||
|
||||
async function listAdminTierLists({ queryText = '', page = 1, limit = 50 } = {}) {
|
||||
async function listAdminTierLists({ queryText = '', page = 1, limit = 50, currentUserId = '' } = {}) {
|
||||
const normalizedLimit = Math.min(Math.max(Number(limit) || 50, 1), 200)
|
||||
const normalizedPage = Math.max(Number(page) || 1, 1)
|
||||
const hasQuery = !!(queryText || '').trim()
|
||||
@@ -762,15 +830,20 @@ async function listAdminTierLists({ queryText = '', page = 1, limit = 50 } = {})
|
||||
|
||||
const total = allItems.length
|
||||
const offset = (normalizedPage - 1) * normalizedLimit
|
||||
const pagedTierLists = allItems.slice(offset, offset + normalizedLimit)
|
||||
const favoriteStats = await getFavoriteStatsForTierListIds(
|
||||
pagedTierLists.map((tierList) => tierList.id),
|
||||
currentUserId
|
||||
)
|
||||
return {
|
||||
tierLists: allItems.slice(offset, offset + normalizedLimit),
|
||||
tierLists: applyFavoriteMetaToTierLists(pagedTierLists, favoriteStats),
|
||||
total,
|
||||
page: normalizedPage,
|
||||
limit: normalizedLimit,
|
||||
}
|
||||
}
|
||||
|
||||
async function findTierListById(id) {
|
||||
async function findTierListById(id, currentUserId = '') {
|
||||
const rows = await query(
|
||||
`
|
||||
SELECT
|
||||
@@ -797,7 +870,10 @@ async function findTierListById(id) {
|
||||
`,
|
||||
[id]
|
||||
)
|
||||
return mapTierListRow(rows[0])
|
||||
const tierList = mapTierListRow(rows[0])
|
||||
if (!tierList) return null
|
||||
const favoriteStats = await getFavoriteStatsForTierListIds([tierList.id], currentUserId)
|
||||
return applyFavoriteMetaToTierLists([tierList], favoriteStats)[0]
|
||||
}
|
||||
|
||||
async function deleteTierList(id) {
|
||||
@@ -832,7 +908,7 @@ async function deleteCustomItems(ids) {
|
||||
}
|
||||
|
||||
async function saveTierList({ id, authorId, gameId, title, thumbnailSrc = '', description, isPublic, groups, pool }) {
|
||||
const existing = id ? await findTierListById(id) : null
|
||||
const existing = id ? await findTierListById(id, authorId) : null
|
||||
|
||||
if (existing) {
|
||||
await query(
|
||||
@@ -843,7 +919,7 @@ async function saveTierList({ id, authorId, gameId, title, thumbnailSrc = '', de
|
||||
`,
|
||||
[title, thumbnailSrc, description || '', isPublic ? 1 : 0, serializeJson(groups), serializeJson(pool), now(), existing.id]
|
||||
)
|
||||
return findTierListById(existing.id)
|
||||
return findTierListById(existing.id, authorId)
|
||||
}
|
||||
|
||||
const createdAt = now()
|
||||
@@ -856,7 +932,15 @@ async function saveTierList({ id, authorId, gameId, title, thumbnailSrc = '', de
|
||||
`,
|
||||
[id, authorId, gameId, title, thumbnailSrc, description || '', isPublic ? 1 : 0, serializeJson(groups), serializeJson(pool), createdAt, createdAt]
|
||||
)
|
||||
return findTierListById(id)
|
||||
return findTierListById(id, authorId)
|
||||
}
|
||||
|
||||
async function favoriteTierList({ userId, tierListId }) {
|
||||
await query('INSERT IGNORE INTO favorite_tierlists (user_id, tierlist_id, created_at) VALUES (?, ?, ?)', [userId, tierListId, now()])
|
||||
}
|
||||
|
||||
async function unfavoriteTierList({ userId, tierListId }) {
|
||||
await query('DELETE FROM favorite_tierlists WHERE user_id = ? AND tierlist_id = ?', [userId, tierListId])
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -890,6 +974,8 @@ module.exports = {
|
||||
listUserTierLists,
|
||||
listAdminTierLists,
|
||||
findTierListById,
|
||||
favoriteTierList,
|
||||
unfavoriteTierList,
|
||||
deleteTierList,
|
||||
findCustomItemsByIds,
|
||||
deleteCustomItems,
|
||||
|
||||
Reference in New Issue
Block a user