56 lines
3.1 KiB
JavaScript
56 lines
3.1 KiB
JavaScript
import { getPostgresClient } from './postgres-client'
|
|
import { createPostSummary } from '../../composables/createPostSummary.js'
|
|
|
|
/**
|
|
* 회원의 게시글·댓글을 합쳐 최신순으로 검색하고 페이지 조회한다.
|
|
* @param {{ memberId: string, type: 'all'|'post'|'comment', q: string, page: number, pageSize: number }} options - 조회 조건
|
|
* @returns {Promise<Object>} 통합 작성 이력과 페이지 정보
|
|
*/
|
|
export const listMemberActivityForAdmin = async ({ memberId, type, q, page, pageSize }) => {
|
|
const sql = getPostgresClient()
|
|
if (!sql) return { items: [], pagination: { page: 1, pageSize, total: 0, pageCount: 1 } }
|
|
|
|
return sql.begin(async (transaction) => {
|
|
await transaction`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY`
|
|
/**
|
|
* 집계와 목록에 동일한 작성자·종류·검색 조건을 적용한다.
|
|
* @returns {Object} SQL 조회 조각
|
|
*/
|
|
const activityQuery = () => transaction`
|
|
SELECT 'post'::text AS kind, posts.id, posts.id AS "postId", posts.title AS "postTitle",
|
|
posts.slug AS "postSlug", posts.status AS "postStatus", posts.published_at AS "publishedAt",
|
|
posts.created_at AS "createdAt", users.username,
|
|
LEFT(COALESCE(NULLIF(posts.excerpt, ''), posts.content, ''), 1000) AS body
|
|
FROM posts INNER JOIN users ON users.id = posts.author_id
|
|
WHERE posts.author_id = ${memberId} AND ${type} <> 'comment'
|
|
AND (${q} = '' OR strpos(lower(concat_ws(' ', posts.title, posts.slug, posts.excerpt, posts.content)), lower(${q})) > 0)
|
|
UNION ALL
|
|
SELECT 'comment'::text AS kind, comments.id, posts.id AS "postId", posts.title AS "postTitle",
|
|
posts.slug AS "postSlug", posts.status AS "postStatus", posts.published_at AS "publishedAt",
|
|
comments.created_at AS "createdAt", users.username, comments.body
|
|
FROM comments
|
|
INNER JOIN posts ON posts.id = comments.post_id
|
|
INNER JOIN users ON users.id = comments.user_id
|
|
WHERE comments.user_id = ${memberId} AND ${type} <> 'post'
|
|
AND (${q} = '' OR strpos(lower(concat_ws(' ', comments.body, posts.title)), lower(${q})) > 0)
|
|
`
|
|
const [count] = await transaction`SELECT COUNT(*)::int AS total FROM (${activityQuery()}) AS activity`
|
|
const total = Number(count.total)
|
|
const pageCount = Math.max(1, Math.ceil(total / pageSize))
|
|
const currentPage = Math.min(page, pageCount)
|
|
const rows = await transaction`
|
|
SELECT * FROM (${activityQuery()}) AS activity
|
|
ORDER BY "createdAt" DESC, kind ASC, id DESC
|
|
LIMIT ${pageSize} OFFSET ${(currentPage - 1) * pageSize}
|
|
`
|
|
const items = rows.map((row) => ({
|
|
...row,
|
|
body: row.kind === 'post' ? createPostSummary('', row.body, { maxLength: 320 }) : row.body,
|
|
publicUrl: row.postStatus === 'members' || (row.postStatus === 'published' && (!row.publishedAt || row.publishedAt <= new Date()))
|
|
? `/post/${encodeURIComponent(row.postSlug)}${row.kind === 'comment' ? '#comments' : ''}`
|
|
: null
|
|
}))
|
|
return { items, pagination: { page: currentPage, pageSize, total, pageCount } }
|
|
})
|
|
}
|