| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- // box-mgnt-api/src/mgnt-backend/feature/channel/channel.service.ts
- import {
- Injectable,
- BadRequestException,
- NotFoundException,
- } from '@nestjs/common';
- import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
- import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
- import { CacheSyncService } from '../../../cache-sync/cache-sync.service';
- import {
- CacheEntityType,
- CacheOperation,
- } from '../../../cache-sync/cache-sync.types';
- import {
- CreateChannelDto,
- ListChannelDto,
- UpdateChannelDto,
- } from './channel.dto';
- import { nowSecBigInt } from '@box/common/time/time.util';
- @Injectable()
- export class ChannelService {
- constructor(
- private readonly mongoPrismaService: MongoPrismaService,
- private readonly cacheSyncService: CacheSyncService,
- ) {}
- /**
- * Current epoch seconds (BigInt) for persisted timestamps.
- *
- * NOTE:
- * - We now keep `createAt`/`updateAt` as BigInt seconds.
- */
- private now(): bigint {
- return nowSecBigInt();
- }
- private trimOptional(value?: string | null) {
- if (value === undefined) return undefined;
- if (value === null) return null;
- return typeof value === 'string' ? value.trim() : value;
- }
- async create(dto: CreateChannelDto) {
- // Check for duplicate channel name
- const existingChannel = await this.mongoPrismaService.channel.findFirst({
- where: { name: dto.name },
- });
- if (existingChannel) {
- throw new BadRequestException(
- `Channel with name "${dto.name}" already exists`,
- );
- }
- // Check for duplicate channelId
- const existingChannelId = await this.mongoPrismaService.channel.findFirst({
- where: { channelId: dto.channelId },
- });
- if (existingChannelId) {
- throw new BadRequestException(
- `Channel with channelId "${dto.channelId}" already exists`,
- );
- }
- const isDefault = (await this.mongoPrismaService.channel.count()) === 0;
- const now = this.now();
- const channel = await this.mongoPrismaService.channel.create({
- data: {
- channelId: dto.channelId,
- name: dto.name,
- landingUrl: dto.landingUrl,
- videoCdn: this.trimOptional(dto.videoCdn) ?? null,
- coverCdn: this.trimOptional(dto.coverCdn) ?? null,
- clientName: this.trimOptional(dto.clientName) ?? null,
- clientNotice: this.trimOptional(dto.clientNotice) ?? null,
- remark: this.trimOptional(dto.remark) ?? null,
- isDefault,
- categories: (dto.categories as any) || null,
- tags: (dto.tags as any) || null,
- tagNames: dto.tagNames || [],
- createAt: now,
- updateAt: now,
- },
- });
- // Auto-schedule cache refresh
- await this.cacheSyncService.scheduleChannelRefreshAll();
- // Schedule channel-with-categories rebuild for this channel
- await this.cacheSyncService.scheduleAction({
- entityType: CacheEntityType.CHANNEL,
- operation: CacheOperation.REFRESH,
- payload: { channelId: channel.id },
- });
- return channel;
- }
- async update(dto: UpdateChannelDto) {
- // Check for duplicate channel name (excluding current channel)
- const duplicateChannel = await this.mongoPrismaService.channel.findFirst({
- where: {
- name: dto.name,
- id: { not: dto.id },
- },
- });
- if (duplicateChannel) {
- throw new BadRequestException(
- `Channel with name "${dto.name}" already exists`,
- );
- }
- // check for duplicate channelId (excluding current channel)
- const duplicateChannelId = await this.mongoPrismaService.channel.findFirst({
- where: {
- channelId: dto.channelId,
- id: { not: dto.id },
- },
- });
- if (duplicateChannelId) {
- throw new BadRequestException(
- `Channel with channelId "${dto.channelId}" already exists`,
- );
- }
- const now = this.now();
- try {
- const channel = await this.mongoPrismaService.channel.update({
- where: { id: dto.id },
- data: {
- channelId: dto.channelId,
- name: dto.name,
- landingUrl: dto.landingUrl,
- videoCdn: this.trimOptional(dto.videoCdn) ?? null,
- coverCdn: this.trimOptional(dto.coverCdn) ?? null,
- clientName: this.trimOptional(dto.clientName) ?? null,
- clientNotice: this.trimOptional(dto.clientNotice) ?? null,
- remark: this.trimOptional(dto.remark) ?? null,
- categories: (dto.categories as any) || null,
- tags: (dto.tags as any) || null,
- tagNames: dto.tagNames || [],
- updateAt: now,
- },
- });
- // Auto-schedule cache refresh
- await this.cacheSyncService.scheduleChannelRefreshAll();
- // Schedule channel-with-categories rebuild for this channel
- await this.cacheSyncService.scheduleAction({
- entityType: CacheEntityType.CHANNEL,
- operation: CacheOperation.REFRESH,
- payload: { channelId: channel.id },
- });
- return channel;
- } catch (e) {
- if (e instanceof PrismaClientKnownRequestError && e.code === 'P2025') {
- throw new NotFoundException('Channel not found');
- }
- throw e;
- }
- }
- async findOne(id: string) {
- const row = await this.mongoPrismaService.channel.findUnique({
- where: { id },
- });
- if (!row) {
- throw new NotFoundException('Channel not found');
- }
- return row;
- }
- async list(dto: ListChannelDto) {
- const where: any = {};
- if (dto.name) {
- where.name = { contains: dto.name, mode: 'insensitive' };
- }
- // Defensive guards for pagination
- const page = dto.page && dto.page > 0 ? dto.page : 1;
- const size = dto.size && dto.size > 0 ? dto.size : 10;
- const [total, data] = await this.mongoPrismaService.$transaction([
- this.mongoPrismaService.channel.count({ where }),
- this.mongoPrismaService.channel.findMany({
- where,
- orderBy: { updateAt: 'desc' },
- skip: (page - 1) * size,
- take: size,
- }),
- ]);
- return {
- total,
- data,
- totalPages: Math.ceil(total / size),
- page,
- size,
- };
- }
- async remove(id: string) {
- try {
- await this.mongoPrismaService.channel.delete({ where: { id } });
- // Auto-schedule cache refresh
- await this.cacheSyncService.scheduleChannelRefreshAll();
- return { message: 'Deleted' };
- } catch (e) {
- if (e instanceof PrismaClientKnownRequestError && e.code === 'P2025') {
- throw new NotFoundException('Channel not found');
- }
- throw e;
- }
- }
- }
|