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

#49 Feat-[store Apis] #53

Open
wants to merge 3 commits into
base: master
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
42 changes: 42 additions & 0 deletions src/inventory/dto/getInventoryItems.response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ApiProperty } from '@nestjs/swagger';

export class itemDto {
@ApiProperty({
name: 'itemId',
description: '아이템 id',
example: 1,
})
itemId: number;

@ApiProperty({
name: 'isUsed',
description: '아이템의 사용여부',
example: false,
})
isUsed: boolean;

@ApiProperty({
name: 'name',
description: '아이템 이름',
example: '감자칩',
})
name: string;

@ApiProperty({
name: 'price',
description: '아이템의 가격',
example: 1000,
})
price: number;

@ApiProperty({
name: 'comment',
description: '아이템 설명',
example: '짭잘하고 바삭한 맛있는 감자칩이다',
})
comment: string;
}

export class getInventoryItemsResponseDto {
items: itemDto[];
}
26 changes: 26 additions & 0 deletions src/inventory/inventory.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { InventoryService } from './inventory.service';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
import { getInventoryItemsResponseDto } from './dto/getInventoryItems.response.dto';

@ApiTags('inventory')
@Controller('api/inventory')
export class InventoryController {
constructor(private inventorySercie: InventoryService) {}

@UseGuards(JwtAuthGuard)
@Get('/')
@ApiOperation({
summary: '인벤토리 아이템 가져오기',
description: '인벤토리에 있는 모든 사용자 아이템을 가져옵니다.',
})
@ApiResponse({
status: 200,
description: '성공',
type: getInventoryItemsResponseDto,
})
async getInventoryItems(@Req() req) {
return await this.inventorySercie.getInventoryItems(req.userId);
}
}
11 changes: 10 additions & 1 deletion src/inventory/inventory.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Users } from 'src/entities/user';
import { InventoryService } from './inventory.service';
import { InventoryController } from './inventory.controller';
import { Inventory } from 'src/entities/inventory';

@Module({})
@Module({
imports: [TypeOrmModule.forFeature([Users, Inventory])],
controllers: [InventoryController, InventoryService],
providers: [InventoryService],
})
export class InventoryModule {}
40 changes: 40 additions & 0 deletions src/inventory/inventory.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Inventory } from 'src/entities/inventory';

import { DataSource, Repository } from 'typeorm';
import { itemDto } from './dto/getInventoryItems.response.dto';

@Injectable()
export class InventoryService {
constructor(
private dataSource: DataSource,
@InjectRepository(Inventory)
private inventoryRepository: Repository<Inventory>,
) {}

async getInventoryItems(userId: number) {
const inventoryItems = await this.inventoryRepository.find({
where: { itemOwnerId: userId },
relations: ['ItemId'],
});

const result: itemDto[] = [];

inventoryItems.forEach((e) => {
const item: itemDto = {
itemId: e.itemId,
isUsed: e.isUsed,
name: e.ItemId.name,
price: e.ItemId.price,
comment: e.ItemId.comment,
};

result.push(item);
});

return {
items: result,
};
}
}
13 changes: 13 additions & 0 deletions src/store/dto/buyItem.request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsNumber } from 'class-validator';

export class buyItemRequestDto {
@IsNumber()
@IsNotEmpty()
@ApiProperty({
name: 'itemId',
description: '아이템 아이디',
example: 1,
})
itemId: number;
}
18 changes: 18 additions & 0 deletions src/store/store.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { StoreController } from './store.controller';

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

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

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

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
30 changes: 30 additions & 0 deletions src/store/store.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common';
import {
ApiBody,
ApiHeader,
ApiOperation,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
import { buyItemRequestDto } from './dto/buyItem.request.dto';
import { StoreService } from './store.service';

@ApiTags('store')
@ApiHeader({ name: 'access', description: 'access token' })
@Controller('api/store')
export class StoreController {
constructor(private storeService: StoreService) {}

@UseGuards(JwtAuthGuard)
@Post('/')
@ApiOperation({ summary: '상점에서 아이템 구매' })
@ApiBody({ type: buyItemRequestDto })
@ApiResponse({
status: 200,
description: '성공',
})
async buyItem(@Req() req, @Body() data: buyItemRequestDto) {
await this.storeService.buyItem(req.userId, data.itemId);
}
}
7 changes: 6 additions & 1 deletion src/store/store.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Module } from '@nestjs/common';
import { StoreController } from './store.controller';
import { StoreService } from './store.service';

@Module({})
@Module({
controllers: [StoreController],
providers: [StoreService]
})
export class StoreModule {}
18 changes: 18 additions & 0 deletions src/store/store.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { StoreService } from './store.service';

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

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

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

it('should be defined', () => {
expect(service).toBeDefined();
});
});
60 changes: 60 additions & 0 deletions src/store/store.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Inventory } from 'src/entities/inventory';
import { Users } from 'src/entities/user';
import { Item } from 'src/entities/item';

@Injectable()
export class StoreService {
constructor(
private dataSource: DataSource,
@InjectRepository(Users) private usersRepository: Repository<Users>,
@InjectRepository(Inventory)
private inventoryRepository: Repository<Inventory>,
@InjectRepository(Item)
private itemRepository: Repository<Item>,
) {}

async buyItem(userId: number, itemId: number) {
// 1. get item price
const item = await this.itemRepository.findOne({
where: { id: itemId },
});

if (item) {
throw new BadRequestException('요청하신 아이템은 존재하지 않습니다!');
}

// 2. check user budget
const user = await this.usersRepository.findOne({
where: { id: userId },
});

if (user) {
throw new BadRequestException('잘못된 요청입니다!');
}

if (user.cash < item.price) {
throw new BadRequestException('아이템을 구매할 수 없습니다!');
}

// 3. run transaction
// 3-1. insert info to user table
// 3-2. reduce budget from user
await this.dataSource.manager
.transaction(async (manager) => {
const inventory = new Inventory();
inventory.itemOwnerId = userId;
inventory.itemId = itemId;
inventory.isUsed = false;

user.cash -= item.price;
await manager.save(user);
await manager.save(inventory);
})
.catch((e) => {
throw e;
});
}
}