feat: add hall and seats services

This commit is contained in:
Sergey Krylov 2026-04-19 08:54:39 +03:00
parent 1b0dc92086
commit 336b152fdf
11 changed files with 209 additions and 6 deletions

View File

@ -5,8 +5,10 @@ import { PassportModule } from '@teacinema/passport'
import { AccountModule } from '../modules/account/account.module'
import { AuthModule } from '../modules/auth/auth.module'
import { CategoryModule } from '../modules/category/category.module'
import { HallModule } from '../modules/hall/hall.module'
import { MediaModule } from '../modules/media/media.module'
import { MovieModule } from '../modules/movie/movie.module'
import { SeatModule } from '../modules/seat/seat.module'
import { TheaterModule } from '../modules/theater/theater.module'
import { UsersModule } from '../modules/users/users.module'
import { ObservabilityModule } from '../observability/observability.module'
@ -33,6 +35,8 @@ import { getPassportConfig } from './config'
MovieModule,
CategoryModule,
TheaterModule,
HallModule,
SeatModule,
PassportModule.registerAsync({
useFactory: getPassportConfig,
inject: [ConfigService]

View File

@ -0,0 +1 @@
export * from './rq'

View File

@ -0,0 +1,67 @@
import { Body, Controller, Get, HttpCode, HttpStatus } from '@nestjs/common'
import { ApiProperty } from '@nestjs/swagger'
import type { ListHallsByTheaterRequest } from '@teacinema/contracts/gen/ts/hall'
import { Type } from 'class-transformer'
import {
IsArray,
IsEnum,
IsInt,
IsNotEmpty,
IsNumber,
IsString,
ValidateNested
} from 'class-validator'
enum SeatType {
CHAIR = 'chair',
SOFA2 = 'sofa2',
SOFA3 = 'sofa3'
}
class RowConfig {
@ApiProperty({
example: 1
})
@IsNumber()
public row: number
@ApiProperty({
example: 15
})
@IsNumber()
public columns: number
@ApiProperty({
example: SeatType.CHAIR,
enum: SeatType
})
@IsEnum(SeatType)
public type: SeatType
@ApiProperty({
example: 500
})
@IsInt()
public price: number
}
export class CreateHallRequestDto {
@ApiProperty({
example: 'Зал 1'
})
@IsString()
@IsNotEmpty()
public name: string
@ApiProperty({
example: '123456'
})
@IsString()
@IsNotEmpty()
public theaterId: string
@IsArray()
@ValidateNested({ each: true })
@Type(() => RowConfig)
public layout: RowConfig[]
}

View File

@ -0,0 +1 @@
export * from './create-hall.request.dto'

View File

@ -0,0 +1,44 @@
import {
Body,
Controller,
Get,
HttpCode,
HttpStatus,
Param,
Post
} from '@nestjs/common'
import type { ListHallsByTheaterRequest } from '@teacinema/contracts/gen/ts/hall'
import { Role } from 'src/shared/guards'
import { Protected } from '../../shared/decorators'
import { CreateHallRequestDto } from './dto'
import { HallClientGrpc } from './hall.grpc'
@Controller('halls')
export class HallController {
constructor(private readonly client: HallClientGrpc) {}
@Get()
@HttpCode(HttpStatus.OK)
public async getAll(@Body() data: ListHallsByTheaterRequest) {
const { halls } = await this.client.call('listHallsByTheater', data)
return halls
}
@Get(':id')
@HttpCode(HttpStatus.OK)
public async getById(@Param('id') id: string) {
const { hall } = await this.client.call('getHall', { id })
return hall
}
// @Protected(Role.ADMIN)
@Post()
@HttpCode(HttpStatus.CREATED)
public async create(@Body() dto: CreateHallRequestDto) {
const { hall } = await this.client.call('createHall', dto)
return hall
}
}

View File

@ -0,0 +1,13 @@
import { Injectable } from '@nestjs/common'
import type { ClientGrpc } from '@nestjs/microservices'
import { InjectGrpc } from '@teacinema/common'
import { HallServiceClient } from '@teacinema/contracts/gen/ts/hall'
import { AbstractGrpcClient } from '../../shared/grpc/abstract-grpc.client'
@Injectable()
export class HallClientGrpc extends AbstractGrpcClient<HallServiceClient> {
constructor(@InjectGrpc('HALL_PACKAGE') client: ClientGrpc) {
super(client, 'HallService')
}
}

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common'
import { GrpcModule } from '@teacinema/common'
import { HallController } from './hall.controller'
import { HallClientGrpc } from './hall.grpc'
@Module({
imports: [GrpcModule.register(['HALL_PACKAGE'])],
controllers: [HallController],
providers: [HallClientGrpc]
})
export class HallModule {}

View File

@ -0,0 +1,36 @@
import {
Body,
Controller,
Get,
HttpCode,
HttpStatus,
Param
} from '@nestjs/common'
import { SeatClientGrpc } from './seat.grpc'
@Controller('seats')
export class SeatController {
constructor(private readonly client: SeatClientGrpc) {}
@Get(':hallId/:screeningId')
@HttpCode(HttpStatus.OK)
public async getAll(
@Param('hallId') hallId: string,
@Param('screeningId') screeningId: string
) {
const { seats } = await this.client.call('listSeatsByHall', {
hallId,
screeningId
})
return seats
}
@Get(':id')
@HttpCode(HttpStatus.OK)
public async getById(@Param('id') id: string) {
const { seat } = await this.client.call('getSeat', { id })
return seat
}
}

View File

@ -0,0 +1,13 @@
import { Injectable } from '@nestjs/common'
import type { ClientGrpc } from '@nestjs/microservices'
import { InjectGrpc } from '@teacinema/common'
import { SeatServiceClient } from '@teacinema/contracts/gen/ts/seat'
import { AbstractGrpcClient } from '../../shared/grpc/abstract-grpc.client'
@Injectable()
export class SeatClientGrpc extends AbstractGrpcClient<SeatServiceClient> {
constructor(@InjectGrpc('SEAT_PACKAGE') client: ClientGrpc) {
super(client, 'SeatService')
}
}

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common'
import { GrpcModule } from '@teacinema/common'
import { SeatController } from './seat.controller'
import { SeatClientGrpc } from './seat.grpc'
@Module({
imports: [GrpcModule.register(['SEAT_PACKAGE'])],
controllers: [SeatController],
providers: [SeatClientGrpc]
})
export class SeatModule {}

View File

@ -1997,9 +1997,9 @@
"@sinonjs/commons" "^3.0.1"
"@teacinema/common@^1.0.0":
version "1.4.0"
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcommon/-/1.4.0/common-1.4.0.tgz#9523c8a5a54c54b4756f2bc4db5d2cabb60504f2"
integrity sha512-M2c95xPg/lSG5Vz9tkqheuKzrd5m0HDtZdBEpPVehkTjk81GmSywyZr/HMBEl4GU/cQG93Xza//l3yCP7JnaZg==
version "1.5.0"
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcommon/-/1.5.0/common-1.5.0.tgz#236c1aba248b31773297bf17aad9053116483e93"
integrity sha512-GqGx8jFaMP6ADcZP18ariJtAHbJqLhT9Up80SLwU1fInKUthQb5MzLrTc4ce8jAxbczLVMGmikphIQNYNpvCGg==
dependencies:
"@nestjs/common" "^11.1.16"
"@nestjs/config" "^4.0.3"
@ -2007,9 +2007,9 @@
"@teacinema/contracts" "^1.1.3"
"@teacinema/contracts@^1.0.0", "@teacinema/contracts@^1.1.3":
version "1.3.2"
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.3.2/contracts-1.3.2.tgz#4abd896c2af5030ec049b34f978aa52554e6b03d"
integrity sha512-myXVPXPDOzSxixIAh8LuIMg84ftBHSKht9sySLnztrQfssutg4Hz5590aQIMlgUooHBiC/Gu/6sCUEkfeFOofQ==
version "1.4.1"
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.4.1/contracts-1.4.1.tgz#20feccf1784a41ad6b4b0ebfcc035f991a7473c9"
integrity sha512-CcYU/cUQfBw+KJp8d14RONIZahkbUAYxiLqudHhx/kpEPuoQDO8VMJy6SbUqBM1HKZ1VTtyQWfxm/79PBPMe0g==
dependencies:
"@nestjs/microservices" "^11.1.12"
protoc "33.4.0"