143 lines
3.7 KiB
TypeScript
143 lines
3.7 KiB
TypeScript
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<Article>,
|
|
) {}
|
|
|
|
async create(dto: CreateArticleDto): Promise<Article> {
|
|
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<Article> {
|
|
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<Article> {
|
|
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<Article> {
|
|
const article = await this.findOne(id);
|
|
Object.assign(article, dto);
|
|
return await this.articleRepository.save(article);
|
|
}
|
|
|
|
async remove(id: string): Promise<void> {
|
|
const article = await this.findOne(id);
|
|
await this.articleRepository.remove(article);
|
|
}
|
|
|
|
async syncFromStrapi(
|
|
strapiId: string,
|
|
data: Partial<CreateArticleDto>,
|
|
): Promise<Article> {
|
|
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<void> {
|
|
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}`);
|
|
}
|
|
}
|