Gitea Actions / Deploy (push) Canceled after 5m13s
issue observers Наблюдатели задачи видят её и комментируют, но не редактируют. - таблица issue_observer, наблюдатели — часть сущности Issue - видимость: reporter, assignee или наблюдатель (задачи, история, SSE) - правка/удаление задачи и вложений — только reporter/assignee (403 наблюдателю) - observers в create/update, DELETE /issues/:id/observers/me, фильтр role - история изменений и уведомления новым наблюдателям Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
3.2 KiB
TypeScript
89 lines
3.2 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import { ISSUE_REPOSITORY, type IIssueRepository } from '../../../domain/repositories/issue.repository.interface.js';
|
|
import { IssueNotFoundException } from '../../../domain/exceptions/issue-not-found.exception.js';
|
|
import { IssueEditForbiddenException } from '../../../domain/exceptions/issue-edit-forbidden.exception.js';
|
|
import { UpdateIssueCommand } from './update-issue.command.js';
|
|
import { IssueWithUsersDto } from '../../dto/issue-with-users.dto.js';
|
|
import { IssueEventsService } from '../../services/issue-events.service.js';
|
|
import { IssueUserExpanderService } from '../../services/issue-user-expander.service.js';
|
|
import { IssueAssignmentNotifierService } from '../../services/issue-assignment-notifier.service.js';
|
|
import { IssueParticipantsService } from '../../services/issue-participants.service.js';
|
|
import { IssueUpdatedEvent } from '../../../domain/events/issue-updated.event.js';
|
|
|
|
@Injectable()
|
|
export class UpdateIssueUseCase {
|
|
constructor(
|
|
@Inject(ISSUE_REPOSITORY)
|
|
private readonly issueRepository: IIssueRepository,
|
|
private readonly issueEventsService: IssueEventsService,
|
|
private readonly issueUserExpander: IssueUserExpanderService,
|
|
private readonly issueAssignmentNotifier: IssueAssignmentNotifierService,
|
|
private readonly issueParticipants: IssueParticipantsService,
|
|
) {}
|
|
|
|
async execute(command: UpdateIssueCommand): Promise<IssueWithUsersDto> {
|
|
const issue = await this.issueRepository.findById(command.id, command.userId);
|
|
|
|
if (!issue) {
|
|
throw new IssueNotFoundException(command.id);
|
|
}
|
|
|
|
if (!issue.canBeEditedBy(command.userId)) {
|
|
throw new IssueEditForbiddenException(command.id);
|
|
}
|
|
|
|
const observersBefore = [...issue.observers];
|
|
|
|
if (command.title !== undefined) {
|
|
issue.updateTitle(command.title);
|
|
}
|
|
|
|
if (command.content !== undefined) {
|
|
issue.updateContent(command.content);
|
|
}
|
|
|
|
if (command.status !== undefined) {
|
|
issue.changeStatus(command.status);
|
|
}
|
|
|
|
if (command.priority !== undefined) {
|
|
issue.changePriority(command.priority);
|
|
}
|
|
|
|
if (command.type !== undefined) {
|
|
issue.changeType(command.type);
|
|
}
|
|
|
|
if (command.assignee !== undefined) {
|
|
if (command.assignee === null || command.assignee === '') {
|
|
issue.unassign();
|
|
} else {
|
|
issue.assignTo(command.assignee);
|
|
}
|
|
}
|
|
|
|
if (command.dueDate !== undefined) {
|
|
issue.updateDueDate(command.dueDate);
|
|
}
|
|
|
|
// После assignee: новый исполнитель отбрасывается из наблюдателей
|
|
if (command.observers !== undefined) {
|
|
issue.setObservers(command.observers);
|
|
}
|
|
|
|
await this.issueParticipants.ensureObserversExist(issue.observers.filter((id) => !observersBefore.includes(id)));
|
|
await this.issueRepository.update(issue, command.userId);
|
|
const dto = await this.issueUserExpander.expand(issue);
|
|
|
|
await this.issueParticipants.addToProject(issue);
|
|
|
|
const domainEvents = issue.pullDomainEvents();
|
|
this.issueAssignmentNotifier.notify(issue, domainEvents);
|
|
if (domainEvents.some((event) => event instanceof IssueUpdatedEvent)) {
|
|
this.issueEventsService.emitIssueUpdated(dto);
|
|
}
|
|
|
|
return dto;
|
|
}
|
|
}
|