import { Injectable, NotFoundException } from '@nestjs/common'; import { Prisma } from '@prisma/launcher-client'; import { LauncherPrismaService } from '../launcher-prisma/launcher-prisma.service'; import { CreateManualDocumentDto, ListManualDocumentsQuery, UpdateManualDocumentDto } from './dto'; @Injectable() export class ManualsService { constructor(private readonly launcherPrisma: LauncherPrismaService) {} async list(query: ListManualDocumentsQuery) { const page = toPositiveInt(query.page, 1); const limit = Math.min(toPositiveInt(query.limit, 20), 100); const keyword = query.keyword?.trim(); const where = { ...(query.appKey?.trim() ? { appKey: query.appKey.trim() } : {}), ...(keyword ? { OR: [ { title: { contains: keyword, mode: 'insensitive' as const } }, { body: { contains: keyword, mode: 'insensitive' as const } }, ], } : {}), }; const [documents, total] = await this.launcherPrisma.$transaction([ this.launcherPrisma.launcherManualDocument.findMany({ where, orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }], skip: (page - 1) * limit, take: limit, }), this.launcherPrisma.launcherManualDocument.count({ where }), ]); return { documents: documents.map((document, index) => ({ ...document, no: total - ((page - 1) * limit + index), })), total, page, limit, }; } async publicList(appKey?: string) { const documents = await this.launcherPrisma.launcherManualDocument.findMany({ where: { isPublished: true, ...(appKey?.trim() ? { appKey: appKey.trim() } : {}) }, orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }], select: { id: true, appKey: true, title: true, body: true, updatedAt: true, }, }); return { documents }; } async create(body: CreateManualDocumentDto) { const document = await this.launcherPrisma.launcherManualDocument.create({ data: { appKey: body.appKey.trim(), title: body.title.trim(), body: body.body.trim(), isPublished: body.isPublished ?? true, sortOrder: body.sortOrder ?? 0, }, }); return { document }; } async update(id: string, body: UpdateManualDocumentDto) { await this.ensureExists(id); const document = await this.launcherPrisma.launcherManualDocument.update({ where: { id }, data: { ...(body.appKey === undefined ? {} : { appKey: body.appKey.trim() }), ...(body.title === undefined ? {} : { title: body.title.trim() }), ...(body.body === undefined ? {} : { body: body.body.trim() }), ...(body.isPublished === undefined ? {} : { isPublished: body.isPublished }), ...(body.sortOrder === undefined ? {} : { sortOrder: body.sortOrder }), } as Prisma.LauncherManualDocumentUpdateInput, }); return { document }; } async remove(id: string) { await this.ensureExists(id); await this.launcherPrisma.launcherManualDocument.delete({ where: { id } }); return { ok: true }; } private async ensureExists(id: string) { const document = await this.launcherPrisma.launcherManualDocument.findUnique({ where: { id }, select: { id: true } }); if (!document) throw new NotFoundException('매뉴얼 문서를 찾을 수 없습니다.'); } } function toPositiveInt(value: string | undefined, fallback: number) { const parsed = Number(value); return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback; }