Initial commit: NestJS backend for Child Gold Land
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator'
|
||||
|
||||
export class PurchaseStoreItemDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
productId!: string
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Body, Controller, Get, HttpCode, Inject, Post } from '@nestjs/common'
|
||||
import { CurrentUser } from '../common/decorators/current-user.decorator'
|
||||
import { strictBodyPipe } from '../common/pipes/strict-body.pipe'
|
||||
import { PurchaseStoreItemDto } from './dto/purchase-store-item.dto'
|
||||
import { StoreService } from './store.service'
|
||||
|
||||
@Controller('api/store')
|
||||
export class StoreController {
|
||||
constructor(@Inject(StoreService) private readonly store: StoreService) {}
|
||||
|
||||
@Get('products')
|
||||
getProducts() {
|
||||
return this.store.getProducts()
|
||||
}
|
||||
|
||||
@HttpCode(200)
|
||||
@Post('purchases')
|
||||
purchase(
|
||||
@CurrentUser() userId: string,
|
||||
@Body(strictBodyPipe(PurchaseStoreItemDto)) dto: PurchaseStoreItemDto,
|
||||
) {
|
||||
return this.store.purchaseStoreItem(userId, dto.productId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { DatabaseModule } from '../database/database.module'
|
||||
import { UsersModule } from '../users/users.module'
|
||||
import { StoreController } from './store.controller'
|
||||
import { StoreService } from './store.service'
|
||||
|
||||
@Module({
|
||||
imports: [DatabaseModule, UsersModule],
|
||||
controllers: [StoreController],
|
||||
providers: [StoreService],
|
||||
})
|
||||
export class StoreModule {}
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
ConflictException,
|
||||
Inject,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common'
|
||||
import { DatabaseService } from '../database/database.service'
|
||||
import { STORE_PRODUCTS, type StoreProduct } from '../shared/catalog/store.constants'
|
||||
import { addTransaction } from '../shared/economy/user-economy.helpers'
|
||||
import type { MeResponse } from '../shared/types/user.types'
|
||||
import { UsersService } from '../users/users.service'
|
||||
|
||||
@Injectable()
|
||||
export class StoreService {
|
||||
constructor(
|
||||
@Inject(DatabaseService) private readonly database: DatabaseService,
|
||||
@Inject(UsersService) private readonly users: UsersService,
|
||||
) {}
|
||||
|
||||
getProducts(): StoreProduct[] {
|
||||
return STORE_PRODUCTS
|
||||
}
|
||||
|
||||
purchaseStoreItem(userId: string, productId: string): MeResponse {
|
||||
const product = STORE_PRODUCTS.find((p) => p.id === productId)
|
||||
if (!product) {
|
||||
throw new NotFoundException('محصول پیدا نشد')
|
||||
}
|
||||
|
||||
return this.database.transaction(() => {
|
||||
const state = this.users.getUserState(userId)
|
||||
|
||||
if (state.ownedItems.includes(productId)) {
|
||||
throw new ConflictException('قبلاً خریدی!')
|
||||
}
|
||||
if (state.coins < product.price) {
|
||||
throw new BadRequestException('سکه کافی نداری!')
|
||||
}
|
||||
|
||||
state.coins -= product.price
|
||||
state.ownedItems.push(productId)
|
||||
addTransaction(state, {
|
||||
type: 'expense',
|
||||
title: `خرید ${product.title}`,
|
||||
amount: product.price,
|
||||
unit: 'سکه',
|
||||
})
|
||||
this.users.saveUserState(state)
|
||||
return this.users.toMeResponse(state)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user