comments implement
This commit is contained in:
1 parent
9e0bef2527
commit
4b6ecfbf5d
41 files changed
+1350
-13
No files matched your search
@@ -0,0 +1,39 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { COMMENT_REPOSITORY, type ICommentRepository } from '../../domain/repositories/comment.repository.interface.js';
|
||||
import { ISSUE_REPOSITORY, type IIssueRepository } from '../../../issue/domain/repositories/issue.repository.interface.js';
|
||||
import { Comment } from '../../domain/entities/comment.entity.js';
|
||||
import { CommentNotFoundException } from '../../domain/exceptions/comment-not-found.exception.js';
|
||||
|
||||
@Injectable()
|
||||
export class CommentAccessService {
|
||||
constructor(
|
||||
@Inject(COMMENT_REPOSITORY)
|
||||
private readonly commentRepository: ICommentRepository,
|
||||
@Inject(ISSUE_REPOSITORY)
|
||||
private readonly issueRepository: IIssueRepository,
|
||||
) {}
|
||||
|
||||
/** Видна ли задача пользователю (то же правило, что в IIssueRepository.findById) */
|
||||
async isIssueVisible(issueId: number, userId: string): Promise<boolean> {
|
||||
return (await this.issueRepository.findById(issueId, userId)) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает неудалённый комментарий, если его задача видна пользователю.
|
||||
* Иначе — CommentNotFoundException, чтобы не раскрывать существование
|
||||
* комментариев к чужим задачам.
|
||||
*/
|
||||
async getVisibleComment(id: string, userId: string): Promise<Comment> {
|
||||
const comment = await this.commentRepository.findById(id);
|
||||
if (!comment || comment.isDeleted) {
|
||||
throw new CommentNotFoundException(id);
|
||||
}
|
||||
|
||||
const issue = await this.issueRepository.findById(comment.issueId, userId);
|
||||
if (!issue) {
|
||||
throw new CommentNotFoundException(id);
|
||||
}
|
||||
|
||||
return comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Comment } from '../../domain/entities/comment.entity.js';
|
||||
import { CommentDto } from '../dto/comment.dto.js';
|
||||
import { CommentApplicationMapper } from '../mappers/comment-application.mapper.js';
|
||||
import { GetUsersUseCase } from '../../../user/application/use-cases/get-users/get-users.use-case.js';
|
||||
import { GetUsersQuery } from '../../../user/application/use-cases/get-users/get-users.query.js';
|
||||
|
||||
/** Раскрывает автора комментария (или списка комментариев) в полноценного пользователя */
|
||||
@Injectable()
|
||||
export class CommentAuthorExpanderService {
|
||||
constructor(private readonly getUsersUseCase: GetUsersUseCase) {}
|
||||
|
||||
async expand(comment: Comment): Promise<CommentDto> {
|
||||
const [dto] = await this.expandAll([comment]);
|
||||
return dto!;
|
||||
}
|
||||
|
||||
async expandAll(comments: Comment[]): Promise<CommentDto[]> {
|
||||
if (comments.length === 0) return [];
|
||||
|
||||
const userIds = [...new Set(comments.map((comment) => comment.authorId).filter((id): id is string => Boolean(id)))];
|
||||
const users = userIds.length > 0 ? await this.getUsersUseCase.execute(new GetUsersQuery(userIds)) : [];
|
||||
const usersById = new Map(users.map((user) => [user.id, user]));
|
||||
|
||||
return comments.map((comment) => CommentApplicationMapper.toDto(comment, usersById));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { CommentDto } from '../dto/comment.dto.js';
|
||||
|
||||
export type CommentEventType = 'comment.created' | 'comment.updated' | 'comment.deleted';
|
||||
|
||||
export interface CommentEvent {
|
||||
type: CommentEventType;
|
||||
comment: CommentDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* In-process шина событий comment-модуля для SSE-подписчиков
|
||||
* (по аналогии с IssueEventsService). Работает в пределах одного
|
||||
* инстанса приложения — для нескольких инстансов понадобится Redis pub/sub.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CommentEventsService {
|
||||
private readonly events$ = new Subject<CommentEvent>();
|
||||
|
||||
emit(type: CommentEventType, comment: CommentDto): void {
|
||||
this.events$.next({ type, comment });
|
||||
}
|
||||
|
||||
onEvents(): Observable<CommentEvent> {
|
||||
return this.events$.asObservable();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user