Initial commit: NestJS backend for Child Gold Land

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-02 08:08:11 +02:00
co-authored by Cursor
commit edb79180a1
93 changed files with 11565 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
import { IsNotEmpty, IsString } from 'class-validator'
export class PurchaseStoreItemDto {
@IsString()
@IsNotEmpty()
productId!: string
}
+24
View File
@@ -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)
}
}
+12
View File
@@ -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 {}
+53
View File
@@ -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)
})
}
}