Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fix phone #6

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/api/auth/domain/user.entity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// ** Typeorm Imports
import { Column, Entity, PrimaryGeneratedColumn, Unique } from 'typeorm';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn, Unique } from 'typeorm';

// ** enum, dto, entity Imports
import BaseTimeEntity from 'src/common/entity/BaseTime.Entity';
import { UserRole } from '../dto/user.role';
import { Exclude } from 'class-transformer';
import Phone from 'src/api/phone/domain/phone.entity';

@Entity({ name: 'tbl_user' })
@Unique(['email'])
Expand All @@ -18,6 +19,9 @@ export default class User extends BaseTimeEntity {
@Column({ type: 'varchar', length: 120 })
password: string;

@OneToMany(() => Phone, (phone) => phone.user)
phones: Phone[]

@Column({ nullable: true })
@Exclude()
currentHashedRefreshToken?: string;
Expand Down
94 changes: 94 additions & 0 deletions src/api/phone/controller/phone.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// ** Nest Imports
import {
Body,
Controller,
Post,
Req,
UseGuards,
Get,
Query,
ValidationPipe,
Param,
Patch,
Delete,
} from '@nestjs/common';

// ** Swagger Imports
import {
ApiBody,
ApiOperation,
ApiBearerAuth,
ApiTags,
ApiResponse,
} from '@nestjs/swagger';

// ** enum, dto, entity Imports
import RequestPhoneSaveDto from '../dto/phone.save.dto';
import CommonResponse from 'src/common/dto/api.response';
import PhoneServiceImpl from '../service/phone.service';
import { RequestWithUsernDto } from 'src/common/dto/request.user.dto';

// ** Guard Imports
import JwtAccessGuard from 'src/api/auth/passport/auth.jwt-access.guard';
import { PhoneResponse } from 'src/response/phone.response';
import RequestPhoneFindDto from '../dto/phone.find.';

@ApiTags('Phone')
@Controller('phone')
export default class PhoneController {
constructor(private readonly phoneService: PhoneServiceImpl) {}

@ApiBearerAuth('access-token') // Swagger에서 Access Token 사용
@ApiOperation({ summary: '전화번호 생성' }) // ** Api에 대한 설명
@ApiBody({ type: RequestPhoneSaveDto }) // ** Dto에 대한 설명
@ApiResponse(PhoneResponse.savePhone[200]) // ** Response Example Value
@ApiResponse(PhoneResponse.savePhone[400]) // ** Response Example Value
@UseGuards(JwtAccessGuard) // ** Jwt 검증
@Post()
public async savePhone(
@Body() dto: RequestPhoneSaveDto,
@Req() { user }: RequestWithUsernDto,
): Promise<CommonResponse<any>> {
return await this.phoneService.savePhone(dto, user);
}

@ApiBearerAuth('access-token')
@ApiOperation({ summary: '전화번호 전체 조회' })
@ApiResponse(PhoneResponse.findPhoneList[200])
@UseGuards(JwtAccessGuard)
@Get()
public async findAll(
@Query(ValidationPipe) params: RequestPhoneFindDto,
@Req() { user }: RequestWithUsernDto,
): Promise<CommonResponse<any>> {
return await this.phoneService.findPhoneList(params, user);
}

@ApiBearerAuth('access-token')
@ApiOperation({ summary: '전화번호 업데이트' })
@ApiResponse(PhoneResponse.updatePhone[200])
@UseGuards(JwtAccessGuard)
@Patch()
public async updatePhone(
@Param('id') id:number,
@Body() dto: RequestPhoneSaveDto,
@Req() { user }: RequestWithUsernDto,
): Promise<CommonResponse<any>> {
return await this.phoneService.updatePhone(id, dto, user);
}

@ApiBearerAuth('access-token')
@ApiOperation({ summary: '전화번호 삭제' })
@ApiResponse(PhoneResponse.deletePhone[200])
@UseGuards(JwtAccessGuard)
@Delete()
public async deletePhone(
@Param('id') id:number,
@Body() dto: RequestPhoneSaveDto,
@Req() { user }: RequestWithUsernDto,
): Promise<CommonResponse<any>> {
return await this.phoneService.deletePhone(id, user);
}
}


22 changes: 22 additions & 0 deletions src/api/phone/domain/phone.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// ** Typeorm Imports
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, Unique } from 'typeorm';

// ** enum, dto, entity Imports
import BaseTimeEntity from 'src/common/entity/BaseTime.Entity';
import User from 'src/api/auth/domain/user.entity';

@Entity({ name: 'TB_PHONE_YN' })
@Unique(['number'])
export default class Phone extends BaseTimeEntity {
@PrimaryGeneratedColumn()
id: number;

@Column({ type: 'varchar', length: 30, name: 'phone_name' })
name: string;

@Column({ type: 'varchar', length: 30, name: 'phone_number' })
number: string;

@ManyToOne(() => User, (user) => user.phones)
user: User
}
20 changes: 20 additions & 0 deletions src/api/phone/dto/phone.find..ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ** Swagger Imports
import { ApiProperty } from '@nestjs/swagger';

// ** Pipe Imports
import { IsOptional, IsString } from 'class-validator';

// ** Other Imports
import RequestPagingDto from 'src/common/dto/paging.dto';

export default class RequestPhoneFindDto extends RequestPagingDto {
@ApiProperty({ example: '01033618490', required: false })
@IsOptional()
@IsString()
number: string;

@ApiProperty({ example: '임유나', required: false })
@IsOptional()
@IsString()
name: string;
}
15 changes: 15 additions & 0 deletions src/api/phone/dto/phone.save.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// ** Swagger Imports
import { ApiProperty } from '@nestjs/swagger';

// ** Pipe Imports
import { IsString } from 'class-validator';

export default class RequestPhoneSaveDto {
@ApiProperty({ example: '임유나', type: 'string' })
@IsString()
name: string;

@ApiProperty({ example: '01033618490', type: 'string' })
@IsString()
phone: string;
}
17 changes: 17 additions & 0 deletions src/api/phone/phone.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import PhoneService from './service/phone.service';
import PhoneController from './controller/phone.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeOrmExModule } from 'src/repository/typeOrmEx.module';
import Phone from './domain/phone.entity';
import PhoneRepository from './repository/phone.repository';

@Module({
imports: [
TypeOrmModule.forFeature([Phone]),
TypeOrmExModule.forCustomRepository([PhoneRepository]),
],
controllers: [PhoneController],
providers: [PhoneService],
})
export default class PhoneModule {}
50 changes: 50 additions & 0 deletions src/api/phone/repository/phone.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// ** Typeorm Imports
import { QueryBuilder, Repository } from 'typeorm';

// ** Custom Module Imports
import { CustomRepository } from 'src/repository/typeorm-ex.decorator';
import Phone from '../domain/phone.entity';
import RequestPhoneFindDto from '../dto/phone.find.';

@CustomRepository(Phone)
export default class PhoneRepository extends Repository<Phone> {
public async findAll(dto: RequestPhoneFindDto, userId: number) {
const queryBuilder = this.createQueryBuilder('phone') // ** phone entity 선택
.select(['phone.id', 'phone.name', 'phone.number', 'phone.createdAt']) // ** 원하는 컬럼 선택
.where('phone.userId = :userId', { userId }); // ** 조건문

// ** Request에 페이징이 있으면 where
if (dto.page && dto.pageSize) {
queryBuilder.skip(dto.page * dto.pageSize).take(dto.pageSize);
}

// ** Request에 name 있으면 where
if (dto.name) {
queryBuilder.where('phone.name LIKE :name', {
name: `%${dto.name}%`,
});
}

// ** Request에 number 있으면 where
if (dto.number) {
queryBuilder.where('phone.number LIKE :number', {
number: `%${dto.number}%`,
});
}

// ** 쿼리 결과 리턴
return await queryBuilder.getManyAndCount();

}
public async checkUser(id: number, userId: number) {
const result = await this.createQueryBuilder('phone')
.select(['phone.id', 'phone.userId', 'phone.name', 'phone.number', 'phone.createdAt']) // 원하는 컬럼 선택
.where('phone.id = :id', { id })
.andWhere('phone.userId = :userId', { userId })
.getOne();

return result;
}


}
120 changes: 120 additions & 0 deletions src/api/phone/service/phone.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// ** Nest Imports
import { Injectable, NotFoundException, UnauthorizedException, UseGuards } from '@nestjs/common';
import PhoneRepository from '../repository/phone.repository';

// ** enum, dto, entity, types Imports
import CommonResponse from 'src/common/dto/api.response';
import RequestPhoneSaveDto from '../dto/phone.save.dto';
import { BadRequestException } from 'src/exception/customException';
import { RolesGuard } from 'src/roles/roles.guard';
import User from 'src/api/auth/domain/user.entity';
import RequestPhoneFindDto from '../dto/phone.find.';

@Injectable()
export default class PhoneServiceImpl {
constructor(private readonly phoneRepository: PhoneRepository) {}

public async savePhone(
dto: RequestPhoneSaveDto,
user: User
): Promise<CommonResponse<any>> {
const findPhone = await this.phoneRepository.findOne({
where: { number: dto.phone },
});

if (findPhone) {
throw new BadRequestException('Exist Phone Number');
}

await this.phoneRepository.save(
this.phoneRepository.create({
name: dto.name,
number: dto.phone,
user
}),
);

return CommonResponse.of({
statusCode: 200,
message: '전화번호를 생성합니다.',
});
}

//단일 조회
public async findOnePhone(
id: number,
user: User
): Promise<CommonResponse<any>> {
const phone = await this.phoneRepository.findOne({where: {id,}});

if (!phone) {
throw new NotFoundException('Phone not found');
}

if(id !== user.id){
throw new UnauthorizedException("You do not have permission to access this phone")
}
else{
return CommonResponse.of({
statusCode: 200,
message: '전화번호를 조회합니다.',
data: phone
});
}
}

//전체 조회
public async findPhoneList(
dto: RequestPhoneFindDto,
user: User,
): Promise<CommonResponse<any>> {
const [data, count] = await this.phoneRepository.findAll(dto, user.id);

return CommonResponse.of({
statusCode: 200,
message: '전화번호 리스트를 조회합니다.',
data: { data, count },
});
}


public async updatePhone(
id: number,
dto: RequestPhoneSaveDto,
user: User
): Promise<CommonResponse<any>> {
const updatePhone = await this.phoneRepository.checkUser(id,user.id);

if (!updatePhone) {
throw new NotFoundException('Phone not found');
}

await this.phoneRepository.update({ id }, dto);

return CommonResponse.of({
statusCode: 200,
message: '전화번호를 업데이트합니다.',
});
}


public async deletePhone(
id : number,
user: User,
): Promise<CommonResponse<any>>{

const deletePhone = await this.phoneRepository.checkUser(id, user.id)

if (!deletePhone) {
throw new NotFoundException('Phone not found');
}

await this.phoneRepository.delete(id)

return CommonResponse.of({
statusCode: 200,
message: '전화번호를 삭제합니다.',
});
}

}
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeOrmExModule } from './repository/typeOrmEx.module';
import ViewModule from './api/view/view.module';
import AdapterModule from './api/adapter/adapter.module';
import PhoneModule from './api/phone/phone.module';

@Module({
imports: [
Expand All @@ -35,6 +36,7 @@ import AdapterModule from './api/adapter/adapter.module';
TypeOrmExModule,
LoggerModule,
AuthModule,
PhoneModule,
UploadModule,
ViewModule,
AdapterModule,
Expand Down
8 changes: 4 additions & 4 deletions src/config/env/.dev.env
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
SERVER_PORT=8080

DB_HOST=210.223.18.224
DB_HOST=125.133.34.224
DB_PORT=3306
DB_USERNAME=pino
DB_PASSWORD=qwer1595
DB_DATABASE=test
DB_USERNAME=dice
DB_PASSWORD=qwer1234!@
DB_DATABASE=dice_test

JWT_ACCESS_TOKEN_SECRET=bHV4cm9iby1iYWNrZW5kLW5lc3Rqcy1hY2Nlc3N0b2tlbi1qd3Q=
JWT_REFRESH_TOKEN_SECRET=bHV4cm9iby1iYWNrZW5kLW5lc3Rqcy1yZWZyZXNodG9rZW4tand0
Expand Down
Loading