chore: initialize application scaffold
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@example/api",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nest start --watch",
|
||||
"build": "nest build",
|
||||
"start": "node dist/main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.0",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"pg": "^8.13.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
"@nestjs/schematics": "^11.0.0",
|
||||
"@nestjs/testing": "^11.0.1",
|
||||
"@types/node": "^22.10.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UsersModule } from './modules/users/users.module';
|
||||
|
||||
/** 聚合应用基础设施与业务模块。 */
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
TypeOrmModule.forRootAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
type: 'postgres' as const,
|
||||
host: config.getOrThrow<string>('DATABASE_HOST'),
|
||||
port: Number(config.get<string>('DATABASE_PORT', '5432')),
|
||||
username: config.getOrThrow<string>('DATABASE_USER'),
|
||||
password: config.getOrThrow<string>('DATABASE_PASSWORD'),
|
||||
database: config.getOrThrow<string>('DATABASE_NAME'),
|
||||
autoLoadEntities: true,
|
||||
synchronize: false
|
||||
})
|
||||
}),
|
||||
UsersModule
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
/** 启动 HTTP 服务并配置全局协议规则。 */
|
||||
async function bootstrap(): Promise<void> {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.setGlobalPrefix('api/v1');
|
||||
app.enableCors({ origin: ['http://localhost:5173', 'http://localhost:5174'] });
|
||||
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true }));
|
||||
await app.listen(process.env.API_PORT ?? 3000);
|
||||
}
|
||||
|
||||
void bootstrap();
|
||||
@@ -0,0 +1,15 @@
|
||||
import { IsEmail, IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
||||
|
||||
/** 创建用户的请求参数。 */
|
||||
export class CreateUserDto {
|
||||
/** 用户展示名称。 */
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(50)
|
||||
name!: string;
|
||||
|
||||
/** 用户邮箱地址。 */
|
||||
@IsEmail()
|
||||
@MaxLength(120)
|
||||
email!: string;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
||||
|
||||
/** 用户表对应的领域实体。 */
|
||||
@Entity({ name: 'users' })
|
||||
export class UserEntity {
|
||||
/** 用户主键。 */
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
/** 用户展示名称。 */
|
||||
@Column({ length: 50 })
|
||||
name!: string;
|
||||
|
||||
/** 用户邮箱地址,作为唯一业务标识。 */
|
||||
@Column({ length: 120, unique: true })
|
||||
email!: string;
|
||||
|
||||
/** 创建时间。 */
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
/** 最后更新时间。 */
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt!: Date;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateUserDto } from '../dto/create-user.dto';
|
||||
import { UserEntity } from '../entities/user.entity';
|
||||
|
||||
/** 封装 users 表的持久化操作。 */
|
||||
@Injectable()
|
||||
export class UsersRepository {
|
||||
/** 初始化用户实体仓储。 */
|
||||
constructor(@InjectRepository(UserEntity) private readonly repository: Repository<UserEntity>) {}
|
||||
|
||||
/** 查询全部用户并按创建时间倒序返回。 */
|
||||
findAll(): Promise<UserEntity[]> {
|
||||
return this.repository.find({ order: { createdAt: 'DESC' } });
|
||||
}
|
||||
|
||||
/** 根据邮箱查询单个用户。 */
|
||||
findByEmail(email: string): Promise<UserEntity | null> {
|
||||
return this.repository.findOneBy({ email });
|
||||
}
|
||||
|
||||
/** 创建并保存用户实体。 */
|
||||
async create(data: CreateUserDto): Promise<UserEntity> {
|
||||
return this.repository.save(this.repository.create(data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Body, Controller, Get, Post } from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
/** 提供 users 资源的 REST API。 */
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
/** 注入用户服务。 */
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
/** 响应用户集合资源。 */
|
||||
@Get()
|
||||
findAll(): Promise<UserEntity[]> {
|
||||
return this.usersService.findAll();
|
||||
}
|
||||
|
||||
/** 创建一条用户资源。 */
|
||||
@Post()
|
||||
create(@Body() data: CreateUserDto): Promise<UserEntity> {
|
||||
return this.usersService.create(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import { UsersRepository } from './repositories/users.repository';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
/** 注册用户模块的控制器、服务与数据访问依赖。 */
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([UserEntity])],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService, UsersRepository]
|
||||
})
|
||||
export class UsersModule {}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ConflictException, Injectable } from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import { UsersRepository } from './repositories/users.repository';
|
||||
|
||||
/** 处理用户领域业务规则。 */
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
/** 注入用户数据仓储。 */
|
||||
constructor(private readonly usersRepository: UsersRepository) {}
|
||||
|
||||
/** 获取用户列表。 */
|
||||
findAll(): Promise<UserEntity[]> {
|
||||
return this.usersRepository.findAll();
|
||||
}
|
||||
|
||||
/** 创建用户,并保证邮箱不重复。 */
|
||||
async create(data: CreateUserDto): Promise<UserEntity> {
|
||||
const existingUser = await this.usersRepository.findByEmail(data.email);
|
||||
if (existingUser) {
|
||||
throw new ConflictException('邮箱已被使用');
|
||||
}
|
||||
return this.usersRepository.create(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user