diff --git a/backend/src/core/core.module.ts b/backend/src/core/core.module.ts index f25799d..521f75e 100644 --- a/backend/src/core/core.module.ts +++ b/backend/src/core/core.module.ts @@ -8,6 +8,7 @@ import { SessionModule } from '@/src/module/auth/session/session.module' import { TotpModule } from '@/src/module/auth/totp/totp.module' import { VerificationModule } from '@/src/module/auth/verification/verification.module' import { CategoryModule } from '@/src/module/category/category.module' +import { ChannelModule } from '@/src/module/channel/channel.module' import { ChatModule } from '@/src/module/chat/chat.module' import { CronModule } from '@/src/module/cron/cron.module' import { FollowModule } from '@/src/module/follow/follow.module' @@ -60,6 +61,7 @@ import { RedisModule } from './redis/redis.module' CategoryModule, ChatModule, FollowModule, + ChannelModule, ], }) export class CoreModule {} diff --git a/backend/src/core/graphql/schema.gql b/backend/src/core/graphql/schema.gql index 3fc54b7..d2d8cd3 100644 --- a/backend/src/core/graphql/schema.gql +++ b/backend/src/core/graphql/schema.gql @@ -161,6 +161,8 @@ type Query { findAllCategories: [CategoryModel!]! findAllStreams(filters: FilterInput!): [StreamModel!]! findCategoryBySlug(slug: String!): CategoryModel! + findChannelByUsername(name: String!): UserModel! + findChannelFollowersCount(channelId: String!): Float! findCurrentSession: SessionModel! findMessagesByStream(streamId: String!): [ChatMessageModel!]! findMyFollowers: [FollowModel!]! @@ -168,6 +170,7 @@ type Query { findProfile: UserModel! findRandomCategories: [CategoryModel!]! findRandomStreams: [StreamModel!]! + findRecommendedChannels: [UserModel!]! findSessionsByUser: [SessionModel!]! findSocialLinks: [SocialLinkModel!]! generateTotpSecret: TotpModel! @@ -254,6 +257,8 @@ type UserModel { deactivatedAt: DateTime displayName: String! email: String! + followers: [FollowModel!]! + followings: [FollowModel!]! id: ID! isDeactivated: Boolean! isEmailVerified: Boolean! diff --git a/backend/src/module/auth/account/models/user.model.ts b/backend/src/module/auth/account/models/user.model.ts index 3d03aa2..21248c8 100644 --- a/backend/src/module/auth/account/models/user.model.ts +++ b/backend/src/module/auth/account/models/user.model.ts @@ -1,4 +1,5 @@ import { SocialLinkModel } from '@/src/module/auth/profile/inputs/models/social-link.model' +import { FollowModel } from '@/src/module/follow/model/follow.model' import { StreamModel } from '@/src/module/stream/models/stream.model' import { Field, ID, ObjectType } from '@nestjs/graphql' import { User } from '@prisma/generated' @@ -50,6 +51,12 @@ export class UserModel implements User { @Field(() => StreamModel) stream: StreamModel + @Field(() => [FollowModel]) + followers: FollowModel[] + + @Field(() => [FollowModel]) + followings: FollowModel[] + @Field(() => Date) createdAt: Date diff --git a/backend/src/module/channel/channel.module.ts b/backend/src/module/channel/channel.module.ts new file mode 100644 index 0000000..ad4acfe --- /dev/null +++ b/backend/src/module/channel/channel.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common' +import { ChannelService } from './channel.service' +import { ChannelResolver } from './channel.resolver' + +@Module({ + providers: [ChannelResolver, ChannelService], +}) +export class ChannelModule {} diff --git a/backend/src/module/channel/channel.resolver.ts b/backend/src/module/channel/channel.resolver.ts new file mode 100644 index 0000000..4d084cc --- /dev/null +++ b/backend/src/module/channel/channel.resolver.ts @@ -0,0 +1,23 @@ +import { UserModel } from '@/src/module/auth/account/models/user.model' +import { Args, Query, Resolver } from '@nestjs/graphql' +import { ChannelService } from './channel.service' + +@Resolver('Channel') +export class ChannelResolver { + constructor(private readonly channelService: ChannelService) {} + + @Query(() => [UserModel], { name: 'findRecommendedChannels' }) + public async findRecommended() { + return this.channelService.findRecommendedChannel() + } + + @Query(() => UserModel, { name: 'findChannelByUsername' }) + public async findByUsername(@Args('name') name: string) { + return this.channelService.findByUsername(name) + } + + @Query(() => Number, { name: 'findChannelFollowersCount' }) + public async findFollowersCount(@Args('channelId') channelId: string) { + return this.channelService.findFollowersCountByChannel(channelId) + } +} diff --git a/backend/src/module/channel/channel.service.ts b/backend/src/module/channel/channel.service.ts new file mode 100644 index 0000000..b7e0659 --- /dev/null +++ b/backend/src/module/channel/channel.service.ts @@ -0,0 +1,42 @@ +import { PrismaService } from '@/src/core/prisma/prisma.service' +import { Injectable, NotFoundException } from '@nestjs/common' + +@Injectable() +export class ChannelService { + constructor( + private readonly prismaService: PrismaService, + ) { + } + + public async findRecommendedChannel() { + return this.prismaService.user.findMany({ + where: { isDeactivated: false }, + orderBy: { followings: { _count: 'desc' } }, + include: { stream: true }, + take: 7, + }) + } + + public async findByUsername(name: string) { + const channel = await this.prismaService.user.findUnique({ + where: { name, isDeactivated: false }, + include: { + socialLink: { orderBy: { position: 'desc' } }, + stream: { include: { category: true } }, + followings: true, + }, + }) + + if (!channel) { + throw new NotFoundException('Канал не найден') + } + + return channel + } + + public async findFollowersCountByChannel(channelId: string) { + return this.prismaService.follow.count({ + where: { following: { id: channelId } }, + }) + } +}