import { Injectable, NotFoundException, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Article, ArticleStatus } from './entities'; import { CreateArticleDto, UpdateArticleDto, FindArticlesDto, } from './articles.dto'; @Injectable() export class ArticlesService { private readonly logger = new Logger(ArticlesService.name); constructor( @InjectRepository(Article) private readonly articleRepository: Repository
, ) {} async create(dto: CreateArticleDto): Promise
{ const article = this.articleRepository.create({ ...dto, status: dto.status || ArticleStatus.DRAFT, }); return await this.articleRepository.save(article); } async findAll( dto: FindArticlesDto, ): Promise<{ data: Article[]; total: number }> { const { category, author, tag, status = ArticleStatus.PUBLISHED, search, page = 1, limit = 10, } = dto; const queryBuilder = this.articleRepository .createQueryBuilder('article') .leftJoinAndSelect('article.author', 'author') .leftJoinAndSelect('article.category', 'category') .where('article.status = :status', { status }); if (category) { queryBuilder.andWhere('category.slug = :category', { category }); } if (author) { queryBuilder.andWhere('author.slug = :author', { author }); } if (tag) { queryBuilder.andWhere(':tag = ANY(article.tags)', { tag }); } if (search) { queryBuilder.andWhere( '(article.title ILIKE :search OR article.content ILIKE :search)', { search: `%${search}%` }, ); } const [data, total] = await queryBuilder .orderBy('article.createdAt', 'DESC') .skip((page - 1) * limit) .take(limit) .getManyAndCount(); return { data, total }; } async findOne(id: string): Promise
{ const article = await this.articleRepository.findOne({ where: { id }, relations: ['author', 'category'], }); if (!article) { throw new NotFoundException(`Article with ID ${id} not found`); } return article; } async findBySlug(slug: string): Promise
{ const article = await this.articleRepository.findOne({ where: { slug }, relations: ['author', 'category'], }); if (!article) { throw new NotFoundException(`Article with slug ${slug} not found`); } return article; } async update(id: string, dto: UpdateArticleDto): Promise
{ const article = await this.findOne(id); Object.assign(article, dto); return await this.articleRepository.save(article); } async remove(id: string): Promise { const article = await this.findOne(id); await this.articleRepository.remove(article); } async syncFromStrapi( strapiId: string, data: Partial, ): Promise
{ let article = await this.articleRepository.findOne({ where: { strapiId } }); if (!article) { article = this.articleRepository.create({ strapiId, ...data, status: data.status || ArticleStatus.DRAFT, }); } else { Object.assign(article, data); } return await this.articleRepository.save(article); } async removeByStrapiId(strapiId: string): Promise { const article = await this.articleRepository.findOne({ where: { strapiId } }); if (!article) { this.logger.warn(`Article with strapiId ${strapiId} not found`); return; } await this.articleRepository.remove(article); this.logger.log(`Successfully deleted article with strapiId: ${strapiId}`); } }