Skip to content

Commit

Permalink
feat: nestJS category 模块部分接口
Browse files Browse the repository at this point in the history
fix #170

feat: nestJS category 模块剩余接口实现

fix #170
  • Loading branch information
cumt-robin committed Nov 27, 2024
1 parent f3bf5c7 commit b6ee0a7
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-icons-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nest-server": minor
---

feat: NestJS category 模块接口实现
18 changes: 18 additions & 0 deletions .volta/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"node": {
"index": {
"prefix": "https://cdn.npmmirror.com/binaries/node/"
},
"distro": {
"template": "https://registry.npmmirror.com/-/binary/node/v{{version}}/node-v{{version}}-{{os}}-{{arch}}.zip"
}
},
"npm": {
"index": {
"prefix": "https://registry.npmmirror.com/npm/"
},
"distro": {
"template": "https://registry.npmmirror.com/npm/-/npm-{{version}}.tgz"
}
}
}
13 changes: 13 additions & 0 deletions app/nest-server/ENV.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## 准备环境变量

开发环境可以准备一个`.env.development.local`文件,内容如下:

```
PORT=8003
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=xxx
MYSQL_DATABASE_NAME=blog_db
JWT_SECRET=xxx
```
2 changes: 2 additions & 0 deletions app/nest-server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ValidatorModule } from "./modules/validator/validator.module";
import { UserModule } from "./modules/user/user.module";
import { AuthGuard } from "./guards/auth.guard";
import { APP_GUARD } from "@nestjs/core";
import { CategoryModule } from "./modules/category/category.module";

@Module({
imports: [
Expand All @@ -28,6 +29,7 @@ import { APP_GUARD } from "@nestjs/core";
TagModule,
ValidatorModule,
UserModule,
CategoryModule,
],
controllers: [],
providers: [
Expand Down
6 changes: 3 additions & 3 deletions app/nest-server/src/entities/Article.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Column, Entity, Index, JoinColumn, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
// import { User } from "./User";
// import { ArticleCategory } from "./ArticleCategory";
// import { Comments } from "./Comments";
import { Tag } from "./Tag";
import { Category } from "./Category";

@Index("author_id", ["authorId"], {})
@Entity("article", { schema: "blog_db" })
Expand Down Expand Up @@ -80,8 +80,8 @@ export class Article {
// @JoinColumn([{ name: "author_id", referencedColumnName: "id" }])
// author: User;

// @OneToMany(() => ArticleCategory, (articleCategory) => articleCategory.article)
// articleCategories: ArticleCategory[];
@ManyToMany(() => Category, (category) => category.articles)
categories: Category[];

@ManyToMany(() => Tag, (tag) => tag.articles)
tags: Tag[];
Expand Down
26 changes: 19 additions & 7 deletions app/nest-server/src/entities/Category.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Column, Entity, Index, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Column, Entity, Index, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Article } from "./Article";
// import { ArticleCategory } from "./ArticleCategory";

@Index("category_name", ["categoryName"], { unique: true })
@Index("category_name", ["category_name"], { unique: true })
@Entity("category", { schema: "blog_db" })
export class Category {
@Column("varchar", { name: "category_name", unique: true, length: 50 })
categoryName: string;
category_name: string;

@PrimaryGeneratedColumn({ type: "int", name: "id" })
id: number;
Expand All @@ -14,14 +15,25 @@ export class Category {
name: "create_time",
default: () => "CURRENT_TIMESTAMP",
})
createTime: Date;
create_time: Date;

@Column("datetime", { name: "update_time", nullable: true })
updateTime: Date | null;
update_time: Date | null;

@Column("varchar", { name: "poster", nullable: true, length: 300 })
poster: string | null;

// @OneToMany(() => ArticleCategory, (articleCategory) => articleCategory.category)
// articleCategories: ArticleCategory[];
@ManyToMany(() => Article, (article) => article.categories)
@JoinTable({
name: "article_category",
joinColumn: {
name: "category_id",
referencedColumnName: "id",
},
inverseJoinColumn: {
name: "article_id",
referencedColumnName: "id",
},
})
articles: Article[];
}
20 changes: 20 additions & 0 deletions app/nest-server/src/modules/category/category.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CategoryController } from './category.controller';
import { CategoryService } from './category.service';

describe('CategoryController', () => {
let controller: CategoryController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CategoryController],
providers: [CategoryService],
}).compile();

controller = module.get<CategoryController>(CategoryController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
30 changes: 30 additions & 0 deletions app/nest-server/src/modules/category/category.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Body, Controller, Get, Put, Query } from "@nestjs/common";
import { CategoryService } from "./category.service";
import { PublicAccess } from "@/decorators/public-access.decorator";
import { FuzzyQueryCategoriesDto, GetAllCategoriesDto, GetCategoryAdminPageDto, UpdateCategoryDto } from "./dto/category.dto";

@Controller("category")
export class CategoryController {
constructor(private readonly categoryService: CategoryService) {}

@PublicAccess()
@Get("/all")
getAllCategories(@Query() query: GetAllCategoriesDto) {
return query.getCount === "1" ? this.categoryService.getAllCategoriesWithArticleCount() : this.categoryService.getAllCategories();
}

@Get("/fuzzy")
fuzzyQueryCategories(@Query() query: FuzzyQueryCategoriesDto) {
return this.categoryService.fuzzyQueryCategories(query.wd);
}

@Get("/admin/page")
getCategoryAdminPage(@Query() query: GetCategoryAdminPageDto) {
return this.categoryService.getCategoryAdminPageWithArticleCount(query);
}

@Put("/admin/update")
updateCategory(@Body() body: UpdateCategoryDto) {
return this.categoryService.updateCategory(body);
}
}
12 changes: 12 additions & 0 deletions app/nest-server/src/modules/category/category.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from "@nestjs/common";
import { CategoryService } from "./category.service";
import { CategoryController } from "./category.controller";
import { Category } from "@/entities/Category";
import { TypeOrmModule } from "@nestjs/typeorm";

@Module({
imports: [TypeOrmModule.forFeature([Category])],
controllers: [CategoryController],
providers: [CategoryService],
})
export class CategoryModule {}
18 changes: 18 additions & 0 deletions app/nest-server/src/modules/category/category.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CategoryService } from './category.service';

describe('CategoryService', () => {
let service: CategoryService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CategoryService],
}).compile();

service = module.get<CategoryService>(CategoryService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
67 changes: 67 additions & 0 deletions app/nest-server/src/modules/category/category.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Category } from "@/entities/Category";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Like, Repository } from "typeorm";
import { GetCategoryAdminPageDto, UpdateCategoryDto } from "./dto/category.dto";

@Injectable()
export class CategoryService {
constructor(@InjectRepository(Category) private readonly categoryRepository: Repository<Category>) {}

async getAllCategories() {
const data = await this.categoryRepository.find();
return {
data,
};
}

async getAllCategoriesWithArticleCount() {
const categories = await this.categoryRepository
.createQueryBuilder("category")
.leftJoin("category.articles", "article")
.select("category.*")
.addSelect("COUNT(article.id)", "category_count")
.groupBy("category.id")
.getRawMany();
return {
data: categories.map((category) => ({
...category,
category_count: parseInt(category.category_count),
})),
};
}

async fuzzyQueryCategories(wd: string) {
const data = await this.categoryRepository.find({ where: { category_name: Like(`%${wd}%`) } });
return {
data,
};
}

async getCategoryAdminPageWithArticleCount(query: GetCategoryAdminPageDto) {
const { pageNo, pageSize } = query;
const builder = this.categoryRepository
.createQueryBuilder("category")
.leftJoin("category.articles", "article")
.select("category.*")
.addSelect("COUNT(article.id)", "article_count")
.groupBy("category.id")
.offset((pageNo - 1) * pageSize)
.limit(pageSize);

const [data, total] = await Promise.all([builder.getRawMany(), builder.getCount()]);

return {
data: data.map((category) => ({
...category,
article_count: parseInt(category.article_count),
})),
total,
};
}

async updateCategory(body: UpdateCategoryDto) {
const { id, category_name, poster } = body;
await this.categoryRepository.update(id, { category_name, poster });
}
}
39 changes: 39 additions & 0 deletions app/nest-server/src/modules/category/dto/category.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { QueryPositiveInt } from "@/decorators/query-number.decorator";
import { IsIn, IsInt, IsNotEmpty, IsOptional, IsPositive, isPositive, IsString, ValidateIf } from "class-validator";

export class GetAllCategoriesDto {
@IsOptional()
@IsString()
@IsIn(["0", "1"], { message: "必须是0或1" })
getCount?: "0" | "1";
}

export class FuzzyQueryCategoriesDto {
@IsString()
@IsNotEmpty({ message: "不能为空" })
wd: string;
}

export class GetCategoryAdminPageDto {
@IsOptional()
@QueryPositiveInt(1)
pageNo: number = 1;

@IsOptional()
@QueryPositiveInt(10)
pageSize: number = 10;
}

export class UpdateCategoryDto {
@IsInt()
@IsPositive()
id?: number;

@IsString()
@IsNotEmpty()
category_name: string;

@IsOptional()
@IsString()
poster?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class Category {}

0 comments on commit b6ee0a7

Please sign in to comment.