add base service

This commit is contained in:
liuyi 2025-06-04 11:14:19 +08:00
parent 6f581ad383
commit 3fa9ecc33a
7 changed files with 61 additions and 60 deletions

View File

@ -60,6 +60,6 @@ export class CategoryController {
@Delete(':id') @Delete(':id')
@SerializeOptions({ groups: ['category-detail'] }) @SerializeOptions({ groups: ['category-detail'] })
async delete(@Param('id', new ParseUUIDPipe()) id: string) { async delete(@Param('id', new ParseUUIDPipe()) id: string) {
return this.service.delete(id); return this.service.delete([id]);
} }
} }

View File

@ -2,33 +2,23 @@ import { Injectable } from '@nestjs/common';
import { isNil, omit } from 'lodash'; import { isNil, omit } from 'lodash';
import { EntityNotFoundError } from 'typeorm'; import { EntityNotFoundError } from 'typeorm';
import { import { CreateCategoryDto, UpdateCategoryDto } from '@/modules/content/dtos/category.dto';
CreateCategoryDto,
QueryCategoryDto,
UpdateCategoryDto,
} from '@/modules/content/dtos/category.dto';
import { CategoryEntity } from '@/modules/content/entities/category.entity'; import { CategoryEntity } from '@/modules/content/entities/category.entity';
import { CategoryRepository } from '@/modules/content/repositories/category.repository'; import { CategoryRepository } from '@/modules/content/repositories/category.repository';
import { treePaginate } from '@/modules/database/utils'; import { BaseService } from '@/modules/database/base/service';
@Injectable() @Injectable()
export class CategoryService { export class CategoryService extends BaseService<CategoryEntity, CategoryRepository> {
constructor(protected repository: CategoryRepository) {} protected enableTrash = true;
constructor(protected repository: CategoryRepository) {
super(repository);
}
async findTrees() { async findTrees() {
return this.repository.findTrees(); return this.repository.findTrees();
} }
async paginate(options?: QueryCategoryDto) {
const tree = await this.findTrees();
const data = await this.repository.toFlatTrees(tree);
return treePaginate(options, data);
}
async detail(id: string) {
return this.repository.findOneOrFail({ where: { id }, relations: ['parent', 'children'] });
}
async create(data: CreateCategoryDto) { async create(data: CreateCategoryDto) {
const item = await this.repository.save({ const item = await this.repository.save({
...data, ...data,
@ -56,21 +46,6 @@ export class CategoryService {
return item; return item;
} }
async delete(id: string) {
const item = await this.repository.findOneOrFail({
where: { id },
relations: ['parent', 'children'],
});
if (!isNil(item.children) && item.children.length > 0) {
const childrenCategories = [...item.children].map((c) => {
c.parent = item.parent;
return item;
});
await this.repository.save(childrenCategories, { reload: true });
}
return this.repository.remove(item);
}
async getParent(current?: string, parentId?: string) { async getParent(current?: string, parentId?: string) {
if (current === parentId) { if (current === parentId) {
return undefined; return undefined;

View File

@ -11,14 +11,14 @@ import {
} from '@/modules/content/dtos/comment.dto'; } from '@/modules/content/dtos/comment.dto';
import { CommentEntity } from '@/modules/content/entities/comment.entity'; import { CommentEntity } from '@/modules/content/entities/comment.entity';
import { CommentRepository, PostRepository } from '@/modules/content/repositories'; import { CommentRepository, PostRepository } from '@/modules/content/repositories';
import { BaseService } from '@/modules/database/base/service';
import { treePaginate } from '@/modules/database/utils'; import { treePaginate } from '@/modules/database/utils';
@Injectable() @Injectable()
export class CommentService { export class CommentService extends BaseService<CommentEntity, CommentRepository> {
constructor( constructor(protected repository: CommentRepository, protected postRepository: PostRepository) {
protected repository: CommentRepository, super(repository);
protected postRepository: PostRepository, }
) {}
async findTrees(options: QueryCommentTreeDto = {}) { async findTrees(options: QueryCommentTreeDto = {}) {
return this.repository.findTrees({ return this.repository.findTrees({
@ -91,4 +91,8 @@ export class CommentService {
} }
return parent; return parent;
} }
update(data: any, ...others: any[]): Promise<CommentEntity> {
throw new Error('Method not implemented.');
}
} }

View File

@ -11,6 +11,7 @@ import { CategoryRepository } from '@/modules/content/repositories';
import { PostRepository } from '@/modules/content/repositories/post.repository'; import { PostRepository } from '@/modules/content/repositories/post.repository';
import { SearchService } from '@/modules/content/services/search.service'; import { SearchService } from '@/modules/content/services/search.service';
import { SearchType } from '@/modules/content/types'; import { SearchType } from '@/modules/content/types';
import { BaseService } from '@/modules/database/base/service';
import { SelectTrashMode } from '@/modules/database/constants'; import { SelectTrashMode } from '@/modules/database/constants';
import { QueryHook } from '@/modules/database/types'; import { QueryHook } from '@/modules/database/types';
import { paginate } from '@/modules/database/utils'; import { paginate } from '@/modules/database/utils';
@ -24,7 +25,9 @@ type FindParams = {
}; };
@Injectable() @Injectable()
export class PostService { export class PostService extends BaseService<PostEntity, PostRepository, FindParams> {
protected enableTrash = true;
constructor( constructor(
protected repository: PostRepository, protected repository: PostRepository,
protected categoryRepository: CategoryRepository, protected categoryRepository: CategoryRepository,
@ -32,13 +35,15 @@ export class PostService {
protected tagRepository: TagRepository, protected tagRepository: TagRepository,
protected searchService?: SearchService, protected searchService?: SearchService,
protected searchType: SearchType = 'mysql', protected searchType: SearchType = 'mysql',
) {} ) {
super(repository);
}
async paginate(options: QueryPostDto, callback?: QueryHook<PostEntity>) { async paginate(options: QueryPostDto, callback?: QueryHook<PostEntity>) {
if (!isNil(this.searchService) && !isNil(options.search) && this.searchType === 'meili') { if (!isNil(this.searchService) && !isNil(options.search) && this.searchType === 'meili') {
return this.searchService.search( return this.searchService.search(
options.search, options.search,
pick(options, ['trashed', 'page', 'limit']), pick(options, ['trashed', 'page', 'limit', 'isPublished']),
); );
} }
const qb = await this.buildListQuery(this.repository.buildBaseQB(), options, callback); const qb = await this.buildListQuery(this.repository.buildBaseQB(), options, callback);

View File

@ -47,7 +47,7 @@ export class SearchService implements OnModuleInit {
return this.client; return this.client;
} }
async search(text: string, param: SearchOption = {}) { async search(text: string, param: SearchOption = {}): Promise<any> {
const option = { page: 1, limit: 10, trashed: SelectTrashMode.ONLY, ...param }; const option = { page: 1, limit: 10, trashed: SelectTrashMode.ONLY, ...param };
const limit = isNil(option.limit) || option.limit < 1 ? 1 : option.limit; const limit = isNil(option.limit) || option.limit < 1 ? 1 : option.limit;
const page = isNil(option.page) || option.page < 1 ? 1 : option.page; const page = isNil(option.page) || option.page < 1 ? 1 : option.page;

View File

@ -1,19 +1,18 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { omit } from 'lodash'; import { omit } from 'lodash';
import { In } from 'typeorm'; import { CreateTagDto, UpdateTagDto } from '@/modules/content/dtos/tag.dto';
import { CreateTagDto, QueryTagDto, UpdateTagDto } from '@/modules/content/dtos/tag.dto';
import { TagRepository } from '@/modules/content/repositories/tag.repository'; import { TagRepository } from '@/modules/content/repositories/tag.repository';
import { paginate } from '@/modules/database/utils'; import { BaseService } from '@/modules/database/base/service';
import { TagEntity } from '../entities';
@Injectable() @Injectable()
export class TagService { export class TagService extends BaseService<TagEntity, TagRepository> {
constructor(protected repository: TagRepository) {} protected enableTrash = true;
async paginate(options: QueryTagDto) { constructor(protected repository: TagRepository) {
const qb = this.repository.buildBaseQB(); super(repository);
return paginate(qb, options);
} }
async detail(id: string) { async detail(id: string) {
@ -31,11 +30,4 @@ export class TagService {
await this.repository.update(data.id, omit(data, ['id'])); await this.repository.update(data.id, omit(data, ['id']));
return this.detail(data.id); return this.detail(data.id);
} }
async delete(ids: string[]) {
const items = await this.repository.find({
where: { id: In(ids) },
});
return this.repository.remove(items);
}
} }

View File

@ -1,4 +1,4 @@
import { NotFoundException } from '@nestjs/common'; import { ForbiddenException, NotFoundException } from '@nestjs/common';
import { isNil } from 'lodash'; import { isNil } from 'lodash';
import { In, ObjectLiteral, SelectQueryBuilder } from 'typeorm'; import { In, ObjectLiteral, SelectQueryBuilder } from 'typeorm';
@ -127,4 +127,29 @@ export abstract class BaseService<
} }
return this.repository.remove(items); return this.repository.remove(items);
} }
async restore(ids: string[]) {
if (!this.enableTrash) {
throw new ForbiddenException(
`Can not to retore ${this.repository.qbName},because trash not enabled!`,
);
}
const items = await this.repository.find({
where: { id: In(ids) as any },
withDeleted: true,
});
const trashIds = items.filter((o) => !isNil(o.deletedAt)).map((o) => o.id);
if (trashIds.length < 1) {
return [];
}
await this.repository.restore(trashIds);
const qb = await this.buildListQB(this.repository.buildBaseQB(), undefined, async (_) =>
_.andWhereInIds(trashIds),
);
return qb.getMany();
}
abstract create(data: any, ...others: any[]): Promise<T>;
abstract update(data: any, ...others: any[]): Promise<T>;
} }