Compare commits

..

No commits in common. "de0d2fe3337ba954a4afabacc8980cccedecbe0c" and "7988850063ae2a904b3855a9a83b7c23c297fb26" have entirely different histories.

8 changed files with 8 additions and 188 deletions

View File

@ -8,22 +8,17 @@ import {
Patch,
Post,
Query,
SerializeOptions,
UseInterceptors,
ValidationPipe,
} from '@nestjs/common';
import { CreatePostDto, QueryPostDto, UpdatePostDto } from '@/modules/content/dtos/post.dto';
import { PostService } from '@/modules/content/services/post.service';
import { AppInterceptor } from '@/modules/core/providers/app.interceptor';
@UseInterceptors(AppInterceptor)
@Controller('posts')
export class PostController {
constructor(private postService: PostService) {}
@Get()
@SerializeOptions({ groups: ['post-list'] })
async list(
@Query(
new ValidationPipe({
@ -40,13 +35,11 @@ export class PostController {
}
@Get(':id')
@SerializeOptions({ groups: ['post-detail'] })
async show(@Param('id', new ParseUUIDPipe()) id: string) {
return this.postService.detail(id);
}
@Post()
@SerializeOptions({ groups: ['post-detail'] })
async store(
@Body(
new ValidationPipe({
@ -64,7 +57,6 @@ export class PostController {
}
@Patch()
@SerializeOptions({ groups: ['post-detail'] })
async update(
@Body(
new ValidationPipe({
@ -82,7 +74,6 @@ export class PostController {
}
@Delete(':id')
@SerializeOptions({ groups: ['post-detail'] })
async delete(@Param('id', new ParseUUIDPipe()) id: string) {
return this.postService.delete(id);
}

View File

@ -26,20 +26,18 @@ export class QueryPostDto implements PaginateOptions {
@IsOptional()
isPublished?: boolean;
@IsEnum(PostOrder, {
message: `The sorting rule must be one of ${Object.values(PostOrder).join(',')}`,
})
@IsEnum(PostOrder, { message: `` })
@IsOptional()
orderBy: PostOrder;
@Transform(({ value }) => toNumber(value))
@Min(1, { message: 'The current page must be greater than 1.' })
@Min(1, { message: '' })
@IsNumber()
@IsOptional()
page = 1;
@Transform(({ value }) => toNumber(value))
@Min(1, { message: 'The number of data displayed per page must be greater than 1.' })
@Min(1, { message: '' })
@IsNumber()
@IsOptional()
limit = 10;

View File

@ -1,37 +0,0 @@
import {
BaseEntity,
Column,
Entity,
OneToMany,
PrimaryColumn,
Relation,
Tree,
TreeChildren,
TreeParent,
} from 'typeorm';
import { PostEntity } from '@/modules/content/entities/post.entity';
@Entity('content_category')
@Tree('materialized-path')
export class CategoryEntity extends BaseEntity {
@PrimaryColumn({ type: 'varchar', generated: 'uuid', length: 36 })
id: string;
@Column({ comment: '分类名称', unique: true })
name: string;
@Column({ comment: '分类排序', default: 0 })
customOrder: number;
@OneToMany(() => PostEntity, (post) => post.category, { cascade: true })
posts: Relation<PostEntity>[];
depth = 0;
@TreeParent({ onDelete: 'NO ACTION' })
parent: Relation<CategoryEntity> | null;
@TreeChildren({ cascade: true })
children: Relation<CategoryEntity>[];
}

View File

@ -1,42 +0,0 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
ManyToOne,
PrimaryColumn,
Relation,
Tree,
TreeChildren,
TreeParent,
} from 'typeorm';
import { PostEntity } from '@/modules/content/entities/post.entity';
@Entity('content_comment')
@Tree('materialized-path')
export class CommentEntity extends BaseEntity {
@PrimaryColumn({ type: 'varchar', length: 36, generated: 'uuid' })
id: string;
@Column({ comment: '评论内容', type: 'text' })
body: string;
@CreateDateColumn({ comment: '创建时间' })
createdAt: Date;
@ManyToOne(() => PostEntity, (post) => post.comments, {
nullable: false,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
post: Relation<PostEntity>;
depth = 0;
@TreeParent({ onDelete: 'CASCADE' })
parent: Relation<CommentEntity> | null;
@TreeChildren({ cascade: true })
children: Relation<CommentEntity>[];
}

View File

@ -1,80 +1,45 @@
import { Exclude, Expose, Type } from 'class-transformer';
import { Expose } from 'class-transformer';
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
JoinTable,
ManyToMany,
OneToMany,
PrimaryColumn,
Relation,
UpdateDateColumn,
} from 'typeorm';
import { PostBodyType } from '@/modules/content/constants';
import { CategoryEntity } from '@/modules/content/entities/CategoryEntity';
import { CommentEntity } from '@/modules/content/entities/comment.entity';
import { TagEntity } from '@/modules/content/entities/tag.entity';
@Exclude()
@Entity('content_posts')
export class PostEntity extends BaseEntity {
@Expose()
@PrimaryColumn({ type: 'varchar', generated: 'uuid', length: 36 })
id: string;
@Expose()
@Column({ comment: '文章标题' })
title: string;
@Expose({ groups: ['post-detail'] })
@Column({ comment: '文章内容', type: 'text' })
body: string;
@Expose()
@Column({ comment: '文章描述', nullable: true })
summary?: string;
@Expose()
@Expose()
@Column({ comment: '关键字', type: 'simple-array', nullable: true })
keywords?: string[];
keywords?: [];
@Expose()
@Column({ comment: '文章类型', type: 'enum', enum: PostBodyType, default: PostBodyType.HTML })
type: PostBodyType;
@Expose()
@Column({ comment: '发布时间', type: 'varchar', nullable: true })
publishedAt?: Date | null;
@Expose()
@Column({ comment: '自定义文章排序', default: 0 })
customOrder: number;
@Expose()
@Type(() => Date)
@CreateDateColumn({ comment: '创建时间' })
createdAt?: Date;
@Expose()
@Type(() => Date)
@UpdateDateColumn({ comment: '更新时间', nullable: true })
@UpdateDateColumn({ comment: '更新时间' })
updatedAt?: Date;
@Expose()
@OneToMany(() => CategoryEntity, (category) => category.posts, {
nullable: true,
onDelete: 'SET NULL',
})
category: Relation<CategoryEntity>;
@Expose()
@ManyToMany(() => TagEntity, (tag) => tag.posts, { cascade: true })
@JoinTable()
tags: Relation<TagEntity>[];
@OneToMany(() => CommentEntity, (comment) => comment.post, { cascade: true })
comments: Relation<CommentEntity>[];
}

View File

@ -1,20 +0,0 @@
import { Expose } from 'class-transformer';
import { Column, Entity, ManyToMany, PrimaryColumn, Relation } from 'typeorm';
import { PostEntity } from '@/modules/content/entities/post.entity';
@Entity('content_tag')
export class TagEntity {
@PrimaryColumn({ type: 'varchar', generated: 'uuid', length: 36 })
id: string;
@Column({ comment: '标签名称', unique: true })
name: string;
@Column({ comment: '标签描述', nullable: true })
desc?: string;
@Expose()
@ManyToMany(() => PostEntity, (post) => post.tags)
posts: Relation<PostEntity>[];
}

View File

@ -65,7 +65,7 @@ export class PostService {
const { orderBy, isPublished } = options;
if (typeof isPublished === 'boolean') {
isPublished
? qb.where({ publishedAt: Not(IsNull()) })
? qb.where({ publishedAt: Not(IsNull) })
: qb.where({ publishedAt: IsNull() });
}
this.queryOrderBy(qb, orderBy);
@ -84,7 +84,7 @@ export class PostService {
case PostOrder.PUBLISHED:
return qb.orderBy('post.publishedAt', 'DESC');
case PostOrder.CUSTOM:
return qb.orderBy('post.customOrder', 'DESC');
return qb.orderBy('post.custom', 'DESC');
default:
return qb
.orderBy('post.createdAt', 'DESC')

View File

@ -1,35 +0,0 @@
import {
ClassSerializerContextOptions,
ClassSerializerInterceptor,
PlainLiteralObject,
StreamableFile,
} from '@nestjs/common';
import { isArray, isNil, isObject } from 'lodash';
export class AppInterceptor extends ClassSerializerInterceptor {
serialize(
response: PlainLiteralObject | Array<PlainLiteralObject>,
options: ClassSerializerContextOptions,
): PlainLiteralObject | Array<PlainLiteralObject> {
if ((!isObject(response) && !isArray(response)) || response instanceof StreamableFile) {
return response;
}
if (isArray(response)) {
return (response as PlainLiteralObject[]).map((item) => {
return !isObject(item) ? item : this.transformToPlain(item, options);
});
}
if ('meta' in response && 'items' in response) {
const items = !isNil(response.items) && isArray(response.items) ? response.items : [];
return {
...response,
items: (items as PlainLiteralObject[]).map((item) => {
return isObject(item) ? this.transformToPlain(item, options) : item;
}),
};
}
return super.transformToPlain(response, options);
}
}