diff --git a/src/app.module.ts b/src/app.module.ts index 70e8761..3da81e1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,6 +8,7 @@ import { UsersController } from './modules/users/users.controller'; import { ProductsModule } from './modules/products/products.module'; import { AuthModule } from './modules/auth/auth.module'; import { OrdersModule } from './modules/orders/orders.module'; +import { WalletsModule } from './modules/wallets/wallets.module'; @Module({ imports: [ @@ -17,6 +18,7 @@ import { OrdersModule } from './modules/orders/orders.module'; ProductsModule, AuthModule, OrdersModule, + WalletsModule, ], controllers: [AppController, UsersController], providers: [AppService], diff --git a/src/core/constants/index.ts b/src/core/constants/index.ts index c1829d2..518e7f0 100644 --- a/src/core/constants/index.ts +++ b/src/core/constants/index.ts @@ -6,3 +6,4 @@ export const USER_REPOSITORY = 'USER_REPOSITORY'; export const PRODUCT_REPOSITORY = 'PRODUCT_REPOSITORY'; export const ORDER_REPOSITORY = 'ORDER_REPOSITORY'; export const ORDER_STATUSES_REPOSITORY = 'ORDER_STATUSES_REPOSITORY'; +export const WALLET_REPOSITORY = 'WALLET_REPOSITORY'; diff --git a/src/core/database/database.providers.ts b/src/core/database/database.providers.ts index 354b795..804c2a0 100644 --- a/src/core/database/database.providers.ts +++ b/src/core/database/database.providers.ts @@ -5,6 +5,7 @@ import { User } from 'src/modules/users/entities/user.entity'; import { Product } from 'src/modules/products/entities/product.entity'; import { OrderStatuses } from 'src/modules/orders/entities/order-status.entity'; import { Order } from 'src/modules/orders/entities/order.entity'; +import { Wallet } from 'src/modules/wallets/entities/wallet.entity'; export const databaseProviders = [ { @@ -25,7 +26,7 @@ export const databaseProviders = [ config = databaseConfig.development; } const sequelize = new Sequelize(config); - sequelize.addModels([User, Product, Order, OrderStatuses]); + sequelize.addModels([User, Product, Order, OrderStatuses, Wallet]); await sequelize.sync(); return sequelize; }, diff --git a/src/modules/users/entities/user.entity.ts b/src/modules/users/entities/user.entity.ts index 53f9dca..e35ecfd 100644 --- a/src/modules/users/entities/user.entity.ts +++ b/src/modules/users/entities/user.entity.ts @@ -1,5 +1,13 @@ -import { Table, Column, Model, DataType, HasMany } from 'sequelize-typescript'; +import { + Table, + Column, + Model, + DataType, + HasMany, + HasOne, +} from 'sequelize-typescript'; import { Order } from '../../orders/entities/order.entity'; +import { Wallet } from 'src/modules/wallets/entities/wallet.entity'; @Table({ tableName: 'users' }) export class User extends Model { @@ -56,4 +64,7 @@ export class User extends Model { @HasMany(() => Order) orders: Order[]; + + @HasOne(() => Wallet) + wallet: Wallet; } diff --git a/src/modules/wallets/dto/update-wallet.dto.ts b/src/modules/wallets/dto/update-wallet.dto.ts new file mode 100644 index 0000000..c6e94ba --- /dev/null +++ b/src/modules/wallets/dto/update-wallet.dto.ts @@ -0,0 +1,3 @@ +export class UpdateWalletDto { + amount: number; +} diff --git a/src/modules/wallets/entities/wallet.entity.ts b/src/modules/wallets/entities/wallet.entity.ts new file mode 100644 index 0000000..c303820 --- /dev/null +++ b/src/modules/wallets/entities/wallet.entity.ts @@ -0,0 +1,26 @@ +import { + Table, + Column, + Model, + DataType, + ForeignKey, + BelongsTo, +} from 'sequelize-typescript'; +import { User } from 'src/modules/users/entities/user.entity'; + +@Table({ tableName: 'wallets', createdAt: false }) +export class Wallet extends Model { + @Column({ + type: DataType.FLOAT, + allowNull: false, + defaultValue: 0, + }) + balance: number; + + @ForeignKey(() => User) + @Column + userId: number; + + @BelongsTo(() => User) + user: User; +} diff --git a/src/modules/wallets/wallets.controller.ts b/src/modules/wallets/wallets.controller.ts new file mode 100644 index 0000000..c1ee506 --- /dev/null +++ b/src/modules/wallets/wallets.controller.ts @@ -0,0 +1,24 @@ +import { Body, Controller, Get, Param, Patch } from '@nestjs/common'; +import { WalletsService } from './wallets.service'; +import { UUID } from 'crypto'; +import { UpdateWalletDto } from './dto/update-wallet.dto'; + +@Controller('wallets') +export class WalletsController { + constructor(private readonly walletsService: WalletsService) {} + + @Get('balance/:id') + getWallet(@Param('id') id: UUID) { + return this.walletsService.getWallet(id); + } + + @Patch('deposit/:id') + deposit(@Param('id') id: UUID, @Body() updateWalletDto: UpdateWalletDto) { + return this.walletsService.deposit(id, updateWalletDto); + } + + @Patch('withdraw/:id') + withdraw(@Param('id') id: UUID, @Body() updateWalletDto: UpdateWalletDto) { + return this.walletsService.withdraw(id, updateWalletDto); + } +} diff --git a/src/modules/wallets/wallets.module.ts b/src/modules/wallets/wallets.module.ts new file mode 100644 index 0000000..a397c3f --- /dev/null +++ b/src/modules/wallets/wallets.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { WalletsService } from './wallets.service'; +import { WalletsController } from './wallets.controller'; +import { walletsProviders } from './wallets.providers'; + +@Module({ + controllers: [WalletsController], + providers: [WalletsService, ...walletsProviders], + exports: [WalletsService], +}) +export class WalletsModule {} diff --git a/src/modules/wallets/wallets.providers.ts b/src/modules/wallets/wallets.providers.ts new file mode 100644 index 0000000..313edad --- /dev/null +++ b/src/modules/wallets/wallets.providers.ts @@ -0,0 +1,9 @@ +import { Wallet } from './entities/wallet.entity'; +import { WALLET_REPOSITORY } from '../../core/constants'; + +export const walletsProviders = [ + { + provide: WALLET_REPOSITORY, + useValue: Wallet, + }, +]; diff --git a/src/modules/wallets/wallets.service.ts b/src/modules/wallets/wallets.service.ts new file mode 100644 index 0000000..c25c5d1 --- /dev/null +++ b/src/modules/wallets/wallets.service.ts @@ -0,0 +1,45 @@ +import { BadRequestException, Inject, Injectable } from '@nestjs/common'; +import { UUID } from 'crypto'; +import { WALLET_REPOSITORY } from 'src/core/constants'; +import { Wallet } from './entities/wallet.entity'; +import { User } from '../users/entities/user.entity'; +import { UpdateWalletDto } from './dto/update-wallet.dto'; + +@Injectable() +export class WalletsService { + constructor( + @Inject(WALLET_REPOSITORY) private readonly walletRepository: typeof Wallet, + ) {} + + async getWallet(id: UUID) { + return await this.walletRepository.findAll({ + include: { + model: User, + where: { uuid: id }, + attributes: ['firstName'], + }, + }); + } + + async deposit(id: UUID, updateWalletDto: UpdateWalletDto) { + const { amount } = updateWalletDto; + + if (amount < 0) throw new BadRequestException(); + + const [wallet] = await this.getWallet(id); + + wallet.balance += amount; + return wallet.save(); + } + + async withdraw(id: UUID, updateWalletDto: UpdateWalletDto) { + const { amount } = updateWalletDto; + + if (amount < 0) throw new BadRequestException(); + + const [wallet] = await this.getWallet(id); + + wallet.balance -= amount; + return wallet.save(); + } +}