/
viktorphp
/
back-socket
Обзор
Документация
Войти
/
viktorphp
/
back-socket
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/controllers/ancestors/ancestors.controller.ts
279 строк
9 KB
Иванов Виктор Евгеньевич
[feat-tree]: добавить функционал для построения дерева предков
13 апр 2026, 21:34
13 апр 2026, 21:34
52b3f3d
Код
Авторство
О чём код?
import { Controller, Get, Post, Patch, Delete, Param, Body, Query, Request, UseGuards, HttpCode, HttpStatus, UseInterceptors, UploadedFile, ParseIntPipe, DefaultValuePipe, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { memoryStorage } from 'multer'; import { SessionGuard } from 'src/auth/session.guard'; import { AncestorsService } from 'src/services/ancestors/ancestors.service'; import { AncestorTreeService } from 'src/services/ancestors/ancestor-tree.service'; import { CreateAncestorDto } from 'src/dto/ancestors/create-ancestor.dto'; import { UpdateAncestorDto } from 'src/dto/ancestors/update-ancestor.dto'; import { AddRelationDto } from 'src/dto/ancestors/add-relation.dto'; import { CreateContributionDto } from 'src/dto/ancestors/create-contribution.dto'; @Controller('ancestors') @UseGuards(SessionGuard) export class AncestorsController { constructor( private readonly ancestorsService: AncestorsService, private readonly treeService: AncestorTreeService, ) {} // ───────────────────────────────────────────── // CRUD предков // ───────────────────────────────────────────── /** * POST /ancestors * Создаёт нового предка и добавляет в дерево текущего пользователя. */ @Post() @HttpCode(HttpStatus.CREATED) async createAncestor(@Request() req: any, @Body() dto: CreateAncestorDto) { const ancestor = await this.ancestorsService.createAncestor( req.user.id, dto, ); return { status: HttpStatus.CREATED, message: 'Предок создан', ancestor }; } /** * GET /ancestors/search?q=Иванов * Поиск предков по ФИО по всей системе. */ @Get('search') async searchAncestors(@Query('q') query: string) { const ancestors = await this.ancestorsService.searchAncestors(query); return { status: HttpStatus.OK, ancestors }; } /** * GET /ancestors/:id * Карточка предка. */ @Get(':id') async getAncestor(@Param('id') id: string) { const ancestor = await this.ancestorsService.getAncestor(id); return { status: HttpStatus.OK, ancestor }; } /** * PATCH /ancestors/:id * Редактировать предка (только создатель). */ @Patch(':id') async updateAncestor( @Request() req: any, @Param('id') id: string, @Body() dto: UpdateAncestorDto, ) { const ancestor = await this.ancestorsService.updateAncestor( id, req.user.id, dto, ); return { status: HttpStatus.OK, message: 'Предок обновлён', ancestor }; } /** * DELETE /ancestors/:id * Удалить предка (только создатель). */ @Delete(':id') async deleteAncestor(@Request() req: any, @Param('id') id: string) { await this.ancestorsService.deleteAncestor(id, req.user.id); return { status: HttpStatus.OK, message: 'Предок удалён' }; } // ───────────────────────────────────────────── // Дерево // ───────────────────────────────────────────── /** * GET /ancestors/:id/tree?depth=3 * Дерево с центром в конкретном предке. */ @Get(':id/tree') async getTree( @Param('id') id: string, @Query('depth', new DefaultValuePipe(3), ParseIntPipe) depth: number, ) { const tree = await this.treeService.buildTree(id, depth); return { status: HttpStatus.OK, tree }; } // ───────────────────────────────────────────── // Связи // ───────────────────────────────────────────── /** * POST /ancestors/:id/relations * Добавить связь с другим предком. * Если цель — зарегистрированный пользователь → создаёт FamilyRequest. * Иначе → устанавливает связь немедленно. */ @Post(':id/relations') @HttpCode(HttpStatus.CREATED) async addRelation( @Request() req: any, @Param('id') id: string, @Body() dto: AddRelationDto, ) { const result = await this.ancestorsService.addRelation( id, req.user.id, dto, ); if (result.immediate) { return { status: HttpStatus.CREATED, message: 'Связь установлена', }; } return { status: HttpStatus.CREATED, message: 'Запрос отправлен пользователю. Связь будет установлена после подтверждения', request: result.request, }; } /** * POST /ancestors/:id/detach * Разрывает все связи узла, но не удаляет его. * Узел остаётся в UserTree и появится в панели свободных карточек. */ @Post(':id/detach') @HttpCode(HttpStatus.OK) async detachAncestor(@Request() req: any, @Param('id') id: string) { await this.ancestorsService.detachAncestor(id, req.user.id); return { status: HttpStatus.OK, message: 'Связи разорваны' }; } /** * DELETE /ancestors/:id/relations/:relatedId * Удалить связь между двумя предками. */ @Delete(':id/relations/:relatedId') async removeRelation( @Request() req: any, @Param('id') id: string, @Param('relatedId') relatedId: string, ) { await this.ancestorsService.removeRelation(id, relatedId, req.user.id); return { status: HttpStatus.OK, message: 'Связь удалена' }; } // ───────────────────────────────────────────── // Вклады (описания) // ───────────────────────────────────────────── /** * POST /ancestors/:id/contributions * Добавить или обновить своё описание для предка. */ @Post(':id/contributions') async upsertContribution( @Request() req: any, @Param('id') id: string, @Body() dto: CreateContributionDto, ) { const contribution = await this.ancestorsService.upsertContribution( id, req.user.id, dto, ); return { status: HttpStatus.OK, contribution }; } /** * GET /ancestors/:id/contributions * Все описания для предка от разных пользователей. */ @Get(':id/contributions') async getContributions(@Param('id') id: string) { const contributions = await this.ancestorsService.getContributions(id); return { status: HttpStatus.OK, contributions }; } // ───────────────────────────────────────────── // Медиафайлы // ───────────────────────────────────────────── /** * POST /ancestors/:id/media * Загрузить фото или видео для предка. */ @Post(':id/media') @HttpCode(HttpStatus.CREATED) @UseInterceptors(FileInterceptor('file', { storage: memoryStorage() })) async uploadMedia( @Request() req: any, @Param('id') id: string, @UploadedFile() file: Express.Multer.File, ) { const media = await this.ancestorsService.uploadMedia( id, req.user.id, file, ); return { status: HttpStatus.CREATED, message: 'Файл загружен', media }; } /** * GET /ancestors/:id/media * Список медиафайлов предка. */ @Get(':id/media') async getMedia(@Param('id') id: string) { const media = await this.ancestorsService.getAncestorMedia(id); return { status: HttpStatus.OK, media }; } /** * PATCH /ancestors/media/:mediaId/avatar * Установить фото как аватар предка. */ @Patch('media/:mediaId/avatar') async setAvatar(@Request() req: any, @Param('mediaId') mediaId: string) { const media = await this.ancestorsService.setAvatar(mediaId, req.user.id); return { status: HttpStatus.OK, message: 'Аватар обновлён', media }; } /** * GET /ancestors/media/:mediaId/url * Получить временную ссылку на файл. */ @Get('media/:mediaId/url') async getMediaUrl(@Param('mediaId') mediaId: string) { const url = await this.ancestorsService.getMediaUrl(mediaId); return { status: HttpStatus.OK, url }; } /** * DELETE /ancestors/media/:mediaId * Удалить медиафайл (только загрузивший). */ @Delete('media/:mediaId') async deleteMedia(@Request() req: any, @Param('mediaId') mediaId: string) { await this.ancestorsService.deleteMedia(mediaId, req.user.id); return { status: HttpStatus.OK, message: 'Файл удалён' }; } }