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} 통합 작성 이력과 페이지 정보 */ 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 } } }) }