Backend CI / type-check, build, tests (push) Successful in 1m0s
Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
3.2 KiB
TypeScript
110 lines
3.2 KiB
TypeScript
import {
|
|
Inject,
|
|
Injectable,
|
|
InternalServerErrorException,
|
|
Logger,
|
|
forwardRef,
|
|
} from '@nestjs/common'
|
|
import { DatabaseService } from '../database/database.service'
|
|
import { createDefaultUserState } from '../shared/catalog/default-user-state'
|
|
import type { MeResponse, UserState } from '../shared/types/user.types'
|
|
import { GoldLandProgressionService } from '../gold-land/services/progression.service'
|
|
|
|
@Injectable()
|
|
export class UsersService {
|
|
private readonly logger = new Logger(UsersService.name)
|
|
|
|
constructor(
|
|
@Inject(DatabaseService) private readonly database: DatabaseService,
|
|
@Inject(forwardRef(() => GoldLandProgressionService))
|
|
private readonly goldLandProgression: GoldLandProgressionService,
|
|
) {}
|
|
|
|
ensureUser(userId: string): UserState {
|
|
return this.getUserState(userId)
|
|
}
|
|
|
|
getUserState(userId: string): UserState {
|
|
const row = this.database
|
|
.getDb()
|
|
.prepare('SELECT state_json FROM users WHERE id = ?')
|
|
.get(userId) as { state_json: string } | undefined
|
|
|
|
if (row) {
|
|
return this.parseStateJson(row.state_json, userId)
|
|
}
|
|
|
|
const state = createDefaultUserState(userId)
|
|
this.database
|
|
.getDb()
|
|
.prepare('INSERT INTO users (id, state_json, updated_at) VALUES (?, ?, ?)')
|
|
.run(userId, JSON.stringify(state), new Date().toISOString())
|
|
return state
|
|
}
|
|
|
|
saveUserState(state: UserState): UserState {
|
|
this.database
|
|
.getDb()
|
|
.prepare('UPDATE users SET state_json = ?, updated_at = ? WHERE id = ?')
|
|
.run(JSON.stringify(state), new Date().toISOString(), state.id)
|
|
return state
|
|
}
|
|
|
|
toMeResponse(state: UserState): MeResponse {
|
|
return {
|
|
user: {
|
|
id: state.id,
|
|
name: state.name,
|
|
level: state.level,
|
|
xp: state.xp,
|
|
maxXp: state.maxXp,
|
|
coins: state.coins,
|
|
missionProgress: state.missionProgress,
|
|
missionTarget: state.missionTarget,
|
|
},
|
|
wallet: {
|
|
cash: state.cash,
|
|
goldGrams: state.goldGrams,
|
|
},
|
|
progress: {
|
|
completedChallenges: state.completedChallenges,
|
|
completedLessons: state.completedLessons,
|
|
completedGames: state.completedGames,
|
|
claimedRewards: state.claimedRewards,
|
|
ownedItems: state.ownedItems,
|
|
mapLevels: state.mapLevels,
|
|
totalStars: state.totalStars,
|
|
maxStars: state.maxStars,
|
|
},
|
|
achievements: state.achievements,
|
|
transactions: state.transactions,
|
|
market: {
|
|
selectedRange: state.marketSelectedRange,
|
|
},
|
|
}
|
|
}
|
|
|
|
getMe(userId: string): MeResponse {
|
|
const state = this.goldLandProgression.syncUserProgressionState(userId)
|
|
return this.toMeResponse(state)
|
|
}
|
|
|
|
updateProfile(userId: string, name: string): MeResponse {
|
|
return this.database.transaction(() => {
|
|
const state = this.getUserState(userId)
|
|
state.name = name.trim()
|
|
this.saveUserState(state)
|
|
return this.toMeResponse(state)
|
|
})
|
|
}
|
|
|
|
private parseStateJson(raw: string, userId: string): UserState {
|
|
try {
|
|
return JSON.parse(raw) as UserState
|
|
} catch (error) {
|
|
this.logger.error(`Invalid state_json for user ${userId}`, error)
|
|
throw new InternalServerErrorException('داده کاربر نامعتبر است')
|
|
}
|
|
}
|
|
}
|