Skip to content

Commit

Permalink
PRMS-120 [Feat] 약속 단일 조회 및 약속 참여, 취소 API 추가 (#25)
Browse files Browse the repository at this point in the history
* feat: inthash 적용 및 단일 약속 조회 API 추가

* feat: 약속 참여 및 취소 API 추가
  • Loading branch information
jinyongp authored Jan 23, 2024
1 parent 9ee6caa commit 4ffe7d1
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 97 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"customfav",
"dtos",
"fastify",
"hasher",
"inno",
"inthash",
"kakao",
"mikro",
"nestjs",
Expand Down
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"class-transformer": "^0.5.1",
"date-fns": "^2.30.0",
"express": "^4.18.2",
"inthash": "^3.0.3",
"joi": "^17.10.1",
"mysql2": "^3.6.0",
"reflect-metadata": "^0.1.13",
Expand Down
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { UserModule } from '@/modules/user/user.module';
import { jwtConfig } from '@/config/token';
import { PromiseModule } from '@/modules/promise/promise.module';
import { FileUploadModule } from '@/modules/upload/upload.module';
import { CommonModule } from '@/modules/common/common.module';

@Module({
imports: [
Expand All @@ -35,6 +36,7 @@ import { FileUploadModule } from '@/modules/upload/upload.module';
EventModule,
PromiseModule,
FileUploadModule,
CommonModule,
],
controllers: [AppController],
})
Expand Down
17 changes: 17 additions & 0 deletions src/database/migrations/1705999286796-update-location-length.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class UpdateLocationLength1705999286796 implements MigrationInterface {
public async up(runner: QueryRunner): Promise<void> {
await runner.query(
'ALTER TABLE `pm_locations` MODIFY COLUMN `city` varchar(50) NOT NULL'
);
await runner.query(
'ALTER TABLE `pm_locations` MODIFY COLUMN `district` varchar(50) NOT NULL'
);
await runner.query(
'ALTER TABLE `pm_locations` MODIFY COLUMN `address` varchar(100) DEFAULT NULL'
);
}

public async down(_runner: QueryRunner): Promise<void> {}
}
9 changes: 9 additions & 0 deletions src/modules/common/common.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Global, Module } from '@nestjs/common';
import { HasherService } from './services/HasherService.service';

@Module({
providers: [HasherService],
exports: [HasherService],
})
@Global()
export class CommonModule {}
24 changes: 24 additions & 0 deletions src/modules/common/services/HasherService.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Injectable } from '@nestjs/common';
import { Hasher } from 'inthash';

@Injectable()
export class HasherService {
private readonly hasher: Hasher;

constructor() {
this.hasher = new Hasher({
bits: 53,
prime: '7027677444274793',
inverse: '5119353352861145',
xor: '8722148419296618',
});
}

encode(id: number | string): string {
return this.hasher.encode(`${id}`);
}

decode(uid: number | string): string {
return this.hasher.decode(`${uid}`);
}
}
2 changes: 1 addition & 1 deletion src/modules/promise/location.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class LocationEntity {
@Column()
district!: string;

@Column()
@Column({ nullable: true })
address!: string;

@Column({ type: 'double', precision: 10, scale: 8 })
Expand Down
80 changes: 61 additions & 19 deletions src/modules/promise/promise.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import {
BadRequestException,
Body,
Controller,
Delete,
Get,
Patch,
Param,
Post,
Put,
UseGuards,
} from '@nestjs/common';
import {
ApiBadRequestResponse,
ApiBearerAuth,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
Expand All @@ -27,26 +30,39 @@ import {
InputUpdateUserStartLocation,
OutputCreatePromise,
OutputPromiseListItem,
OutputUpdatePromise,
} from './promise.dto';
import { ThemeEntity } from './theme.entity';
import { HttpException } from '@/schema/exception';

@ApiTags('Promise')
@ApiBearerAuth()
@Controller('promise')
@Controller('promises')
export class PromiseController {
constructor(private readonly promiseService: PromiseService) {}

@Get('list')
@Get('')
@UseGuards(JwtAuthGuard)
@ApiOperation({ operationId: 'getPromiseList', summary: '약속 목록' })
@ApiOkResponse({ type: [OutputPromiseListItem], description: '약속 목록' })
@ApiUnauthorizedResponse({ type: HttpException, description: '로그인 필요' })
async list(@AuthUser() user: UserEntity): Promise<OutputPromiseListItem[]> {
async getMyPromises(
@AuthUser() user: UserEntity
): Promise<OutputPromiseListItem[]> {
return this.promiseService.findAllByUser(user.id);
}

@Get(':pid')
@UseGuards(JwtAuthGuard)
@ApiOperation({ operationId: 'getPromise', summary: '약속 상세 정보' })
@ApiOkResponse({ type: OutputPromiseListItem, description: '약속 상세 정보' })
@ApiUnauthorizedResponse({ type: HttpException, description: '로그인 필요' })
@ApiNotFoundResponse({ type: HttpException, description: '약속 없음' })
async getPromiseDetail(
@Param('pid') pid: string
): Promise<OutputPromiseListItem> {
return this.promiseService.findOne(pid);
}

@Post()
@UseGuards(JwtAuthGuard)
@ApiOperation({
Expand All @@ -56,42 +72,68 @@ export class PromiseController {
@ApiOkResponse({ type: OutputCreatePromise, description: '약속 추가 성공' })
@ApiUnauthorizedResponse({ type: HttpException, description: '로그인 필요' })
@ApiBadRequestResponse({ type: HttpException, description: '약속 추가 실패' })
async promise(
async createNewPromise(
@AuthUser() user: UserEntity,
@Body() input: InputCreatePromise
): Promise<OutputCreatePromise> {
this.throwInvalidInputException(input);
return this.promiseService.create(user.id, input);
}

@Patch('start-location')
@Put(':pid')
@UseGuards(JwtAuthGuard)
@ApiOperation({ operationId: 'updateStartLocation', summary: '출발지 설정' })
@ApiOperation({ operationId: 'updatePromise', summary: '약속 수정' })
@ApiOkResponse({ type: OutputCreatePromise, description: '약속 수정 성공' })
@ApiUnauthorizedResponse({ type: HttpException, description: '로그인 필요' })
@ApiBadRequestResponse({ type: HttpException, description: '약속 수정 실패' })
@ApiNotFoundResponse({ type: HttpException, description: '약속 없음' })
async update(
@AuthUser() user: UserEntity,
@Param('pid') pid: string,
@Body() input: InputUpdatePromise
) {
this.throwInvalidInputException(input);
await this.promiseService.update(pid, user.id, input);
}

@Post(':pid/start-location')
@UseGuards(JwtAuthGuard)
@ApiOperation({ operationId: 'setStartLocation', summary: '출발지 설정' })
@ApiOkResponse({ description: '출발지 설정 성공' })
@ApiUnauthorizedResponse({ type: HttpException, description: '로그인 필요' })
@ApiNotFoundResponse({ type: HttpException, description: '약속 없음' })
@ApiBadRequestResponse({
type: HttpException,
description: '출발지 설정 실패',
})
async startLocation(
@AuthUser() user: UserEntity,
@Param('pid') pid: string,
@Body() input: InputUpdateUserStartLocation
) {
this.promiseService.updateStartLocation(user.id, input);
await this.promiseService.updateStartLocation(pid, user.id, input);
}

@Patch(':id')
@Post(':pid/attend')
@UseGuards(JwtAuthGuard)
@ApiOperation({ operationId: 'updatePromise', summary: '약속 수정' })
@ApiOkResponse({ type: OutputCreatePromise, description: '약속 수정 성공' })
@ApiOperation({ operationId: 'attendPromise', summary: '약속 참여' })
@ApiOkResponse({ description: '약속 참여 성공' })
@ApiUnauthorizedResponse({ type: HttpException, description: '로그인 필요' })
@ApiBadRequestResponse({ type: HttpException, description: '약속 수정 실패' })
async update(
@AuthUser() user: UserEntity,
@Body() input: InputUpdatePromise
): Promise<OutputUpdatePromise> {
this.throwInvalidInputException(input);
return this.promiseService.update(user.id, input);
@ApiNotFoundResponse({ type: HttpException, description: '약속 없음' })
@ApiBadRequestResponse({ type: HttpException, description: '약속 참여 실패' })
async attendPromise(@AuthUser() user: UserEntity, @Param('pid') pid: string) {
await this.promiseService.attend(pid, user.id);
}

@Delete(':pid/attend')
@UseGuards(JwtAuthGuard)
@ApiOperation({ operationId: 'cancelPromise', summary: '약속 취소' })
@ApiOkResponse({ description: '약속 취소 성공' })
@ApiUnauthorizedResponse({ type: HttpException, description: '로그인 필요' })
@ApiNotFoundResponse({ type: HttpException, description: '약속 없음' })
@ApiBadRequestResponse({ type: HttpException, description: '약속 취소 실패' })
async cancelPromise(@AuthUser() user: UserEntity, @Param('pid') pid: string) {
await this.promiseService.cancel(pid, user.id);
}

@Get('themes')
Expand Down
31 changes: 14 additions & 17 deletions src/modules/promise/promise.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
import { LocationEntity } from './location.entity';
import { UserEntity } from '../user/user.entity';

class Host extends PickType(UserEntity, ['id', 'username']) {}
class Attendee extends PickType(UserEntity, ['id', 'username']) {}
class Host extends PickType(UserEntity, ['id', 'username', 'profileUrl']) {}
class Attendee extends PickType(UserEntity, ['id', 'username', 'profileUrl']) {}
class OutputDestination extends LocationEntity {}
class InputDestination extends OmitType(LocationEntity, [
'id',
Expand All @@ -17,9 +17,11 @@ class InputDestination extends OmitType(LocationEntity, [
]) {}

export class OutputPromiseListItem extends OmitType(PromiseEntity, [
'id',
'hostId',
'destinationId',
]) {
pid!: string;
themes!: string[];
host!: Host;
destination!: OutputDestination | null;
Expand All @@ -39,25 +41,20 @@ export class InputCreatePromise {
}

export class OutputCreatePromise {
id!: number;
pid!: string;
inviteLink!: string;
}

export class InputUpdatePromise extends PartialType(InputCreatePromise) {
id!: number;
}
export class InputUpdatePromise extends PartialType(InputCreatePromise) {}

export class OutputUpdatePromise {
id!: number;
pid!: string;
}

export class InputUpdateUserStartLocation {
promiseId!: number;
location!: {
city: string;
district: string;
address: string;
latitude: number;
longitude: number;
};
}
export class InputUpdateUserStartLocation extends PickType(LocationEntity, [
'city',
'district',
'address',
'latitude',
'longitude',
]) {}
Loading

0 comments on commit 4ffe7d1

Please sign in to comment.