- IssueAttachment link table (issue <-> file) + migration - GET/POST /issues/:issueId/attachments, DELETE .../attachments/:fileId - Only own uploaded files can be attached; attach is idempotent - Registers issues as a file owner: attached files stay visible to issue participants and are not collected by GC - IssueDetail.attachments GraphQL field via @ResolveField, issue module untouched Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
25 lines
1.3 KiB
TypeScript
25 lines
1.3 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { IssueAttachment } from '../../domain/entities/issue-attachment.entity.js';
|
|
import { IssueAttachmentDto } from '../dto/issue-attachment.dto.js';
|
|
import { GetFilesUseCase } from '../../../file/application/use-cases/get-files/get-files.use-case.js';
|
|
import { GetFilesQuery } from '../../../file/application/use-cases/get-files/get-files.query.js';
|
|
|
|
/** Раскрывает вложения в метаданные файлов из file-модуля */
|
|
@Injectable()
|
|
export class IssueAttachmentExpanderService {
|
|
constructor(private readonly getFilesUseCase: GetFilesUseCase) {}
|
|
|
|
async expandAll(attachments: IssueAttachment[]): Promise<IssueAttachmentDto[]> {
|
|
if (attachments.length === 0) return [];
|
|
|
|
const files = await this.getFilesUseCase.execute(new GetFilesQuery(attachments.map((a) => a.fileId)));
|
|
const filesById = new Map(files.map((file) => [file.id, file]));
|
|
|
|
// Файл могли удалить между запросами — такое вложение просто не показываем
|
|
return attachments.flatMap((attachment) => {
|
|
const file = filesById.get(attachment.fileId);
|
|
return file ? [{ issueId: attachment.issueId, file, attachedAt: attachment.createdAt }] : [];
|
|
});
|
|
}
|
|
}
|