import { Injectable, NotFoundException } from '@nestjs/common'; import { FilterQuery, RequiredEntityData } from '@mikro-orm/core'; import { User } from './entities/user.entity'; import { EntityManager } from '@mikro-orm/postgresql'; import { UpdateUserDto } from './dto/update-user.dto'; import { FindUsersDto } from './dto/find-user.dto'; import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; import { UserRepository } from './repositories/user.repository'; @Injectable() export class UserService { constructor( private readonly userRepository: UserRepository, private readonly em: EntityManager, ) {} async findOrCreateByPhone(phone: string): Promise { let user = await this.userRepository.findOne({ phone }); if (!user) { // firstName is required on the entity; use phone as a sensible default const _raw = { phone, firstName: phone, // keep dates undefined (no value) — cast through unknown to satisfy TS typing birthDate: undefined, marriageDate: undefined, wallet: 0, points: 0, }; const createData = _raw as unknown as RequiredEntityData; user = this.userRepository.create(createData); await this.em.persistAndFlush(user); } return user; } // async updateUser(userId: string, dto: UpdateUserDto): Promise { // const user = await this.em.findOneOrFail(User, userId, {}).catch(() => { // throw new NotFoundException(`User with ID ${userId} not found.`); // }); // this.em.assign(user, { ...dto }); // await this.em.flush(); // return user; // } async findByPhone(phone: string): Promise { return this.userRepository.findOne({ phone }); } async findById(id: string): Promise { return this.userRepository.findOne({ id }); } async create(phone: string): Promise { const _raw = { phone, firstName: phone, birthDate: undefined, marriageDate: undefined, wallet: 0, points: 0, }; const createData = _raw as unknown as RequiredEntityData; const user = this.userRepository.create(createData); await this.em.persistAndFlush(user); return user; } async updateUser(userId: string, dto: UpdateUserDto): Promise { const user = await this.userRepository.findOne({ id: userId }); if (!user) { throw new NotFoundException(`User with ID ${userId} not found.`); } // Normalize date strings into Date objects if provided const assignData: Partial = { ...dto } as Partial; if (dto.birthDate) { assignData.birthDate = new Date(dto.birthDate); } if (dto.marriageDate) { assignData.marriageDate = new Date(dto.marriageDate); } this.em.assign(user, assignData); await this.em.flush(); return user; } async findAll(dto: FindUsersDto): Promise> { const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc' } = dto; // 1. Calculate pagination const offset = (page - 1) * limit; // 2. Build the 'where' filter query const where: FilterQuery = {}; // 4. Add 'search' logic (case-insensitive) if (search) { const searchPattern = `%${search}%`; // $ilike is case-insensitive (PostgreSQL). Use $like for MySQL (often CI by default) where.$or = [ { firstName: { $ilike: searchPattern } }, { lastName: { $ilike: searchPattern } }, { phone: { $ilike: searchPattern } }, ]; } // 5. Execute the query using findAndCount const [users, total] = await this.userRepository.findAndCount(where, { limit, offset, orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, }); // 6. Calculate total pages const totalPages = Math.ceil(total / limit); // 7. Return the paginated result return { data: users, meta: { total, page, limit, totalPages, }, }; } }