Skip to content

Commit 3f746e6

Browse files
committed
feat: implement product management module with CRUD operations and public endpoints
1 parent 7b665d9 commit 3f746e6

8 files changed

Lines changed: 203 additions & 0 deletions

File tree

.DS_Store

-2 KB
Binary file not shown.

src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { UploadModule } from './upload/upload.module';
1111
import { RecruitmentModule } from './recruitment/recruitment.module';
1212
import { ResumeTemplateModule } from './resume-template/resume-template.module';
1313
import { ResumeModule } from './resume/resume.module';
14+
import { ProductModule } from './product/product.module';
1415
const envFilePath = ['.env'];
1516
export const IS_DEV = process.env.RUNNING_ENV !== 'prod';
1617

@@ -40,6 +41,7 @@ if (IS_DEV) {
4041
RecruitmentModule,
4142
ResumeTemplateModule,
4243
ResumeModule,
44+
ProductModule,
4345
],
4446
controllers: [AppController],
4547
providers: [AppService],
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { IsNotEmpty, IsString, IsUrl, IsNumber, Min, Max } from 'class-validator';
2+
3+
export class CreateProductDto {
4+
@IsNotEmpty()
5+
@IsString()
6+
name: string;
7+
8+
@IsNotEmpty()
9+
@IsString()
10+
introduce: string;
11+
12+
@IsNotEmpty()
13+
@IsString()
14+
logo: string;
15+
16+
@IsNotEmpty()
17+
@IsString()
18+
cover: string;
19+
20+
@IsNotEmpty()
21+
@IsString()
22+
@IsUrl({}, { message: 'Product link must be a valid URL' })
23+
link: string;
24+
25+
@IsNotEmpty()
26+
@IsNumber()
27+
@Min(0)
28+
@Max(1)
29+
status: number;
30+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { PartialType } from '@nestjs/mapped-types';
2+
import { CreateProductDto } from './create-product.dto';
3+
4+
export class UpdateProductDto extends PartialType(CreateProductDto) {}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from "typeorm";
2+
3+
@Entity()
4+
export class Product {
5+
@PrimaryGeneratedColumn()
6+
id: number;
7+
8+
// 产品名字
9+
@Column()
10+
name: string;
11+
12+
// 产品介绍
13+
@Column()
14+
introduce: string;
15+
16+
// 产品logo
17+
@Column()
18+
logo: string;
19+
20+
// 产品封面
21+
@Column()
22+
cover: string;
23+
24+
// 产品链接
25+
@Column()
26+
link: string;
27+
28+
// 产品状态, 0: 不展示, 1: 展示
29+
@Column()
30+
status: number;
31+
32+
// 创建时间
33+
@CreateDateColumn()
34+
createdAt: Date;
35+
36+
// 更新时间
37+
@UpdateDateColumn()
38+
updatedAt: Date;
39+
}

src/product/product.controller.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, HttpStatus, HttpCode } from '@nestjs/common';
2+
import { ProductService } from './product.service';
3+
import { CreateProductDto } from './dto/create-product.dto';
4+
import { UpdateProductDto } from './dto/update-product.dto';
5+
import { Product } from './entities/product.entity';
6+
7+
@Controller()
8+
export class ProductController {
9+
constructor(private readonly productService: ProductService) {}
10+
11+
// Admin endpoints
12+
@Post('admin/products')
13+
create(@Body() createProductDto: CreateProductDto) {
14+
return this.productService.create(createProductDto);
15+
}
16+
17+
@Get('admin/products')
18+
findAll(
19+
@Query('page') page?: number,
20+
@Query('limit') limit?: number,
21+
) {
22+
return this.productService.findAll(page, limit);
23+
}
24+
25+
@Get('admin/products/:id')
26+
findOne(@Param('id') id: string) {
27+
return this.productService.findOne(+id);
28+
}
29+
30+
@Patch('admin/products/:id')
31+
update(@Param('id') id: string, @Body() updateProductDto: UpdateProductDto) {
32+
return this.productService.update(+id, updateProductDto);
33+
}
34+
35+
@Delete('admin/products/:id')
36+
@HttpCode(HttpStatus.NO_CONTENT)
37+
async remove(@Param('id') id: string) {
38+
await this.productService.remove(+id);
39+
}
40+
41+
// Public endpoints
42+
@Get('products')
43+
findAllActive(
44+
@Query('page') page?: number,
45+
@Query('limit') limit?: number,
46+
) {
47+
return this.productService.findAllActive(page, limit);
48+
}
49+
50+
@Get('products/:id')
51+
findOnePublic(@Param('id') id: string) {
52+
return this.productService.findOne(+id);
53+
}
54+
}

src/product/product.module.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Module } from '@nestjs/common';
2+
import { TypeOrmModule } from '@nestjs/typeorm';
3+
import { ProductService } from './product.service';
4+
import { ProductController } from './product.controller';
5+
import { Product } from './entities/product.entity';
6+
7+
@Module({
8+
imports: [TypeOrmModule.forFeature([Product])],
9+
controllers: [ProductController],
10+
providers: [ProductService],
11+
exports: [ProductService],
12+
})
13+
export class ProductModule {}

src/product/product.service.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Injectable, NotFoundException } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { Repository } from 'typeorm';
4+
import { CreateProductDto } from './dto/create-product.dto';
5+
import { UpdateProductDto } from './dto/update-product.dto';
6+
import { Product } from './entities/product.entity';
7+
8+
@Injectable()
9+
export class ProductService {
10+
constructor(
11+
@InjectRepository(Product)
12+
private productRepository: Repository<Product>,
13+
) {}
14+
15+
async create(createProductDto: CreateProductDto): Promise<Product> {
16+
const product = this.productRepository.create(createProductDto);
17+
return await this.productRepository.save(product);
18+
}
19+
20+
async findAll(page = 1, limit = 10): Promise<{ items: Product[]; total: number }> {
21+
const [items, total] = await this.productRepository.findAndCount({
22+
order: { createdAt: 'DESC' },
23+
skip: (page - 1) * limit,
24+
take: limit,
25+
});
26+
27+
return { items, total };
28+
}
29+
30+
async findAllActive(page = 1, limit = 10): Promise<{ items: Product[]; total: number }> {
31+
const [items, total] = await this.productRepository.findAndCount({
32+
where: { status: 1 },
33+
order: { createdAt: 'DESC' },
34+
skip: (page - 1) * limit,
35+
take: limit,
36+
});
37+
38+
return { items, total };
39+
}
40+
41+
async findOne(id: number): Promise<Product> {
42+
const product = await this.productRepository.findOne({ where: { id } });
43+
if (!product) {
44+
throw new NotFoundException(`Product with ID ${id} not found`);
45+
}
46+
return product;
47+
}
48+
49+
async update(id: number, updateProductDto: UpdateProductDto): Promise<Product> {
50+
const product = await this.findOne(id);
51+
this.productRepository.merge(product, updateProductDto);
52+
return await this.productRepository.save(product);
53+
}
54+
55+
async remove(id: number): Promise<void> {
56+
const result = await this.productRepository.delete(id);
57+
if (result.affected === 0) {
58+
throw new NotFoundException(`Product with ID ${id} not found`);
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)