141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
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<User> {
|
|
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>;
|
|
|
|
user = this.userRepository.create(createData);
|
|
await this.em.persistAndFlush(user);
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
// async updateUser(userId: string, dto: UpdateUserDto): Promise<User> {
|
|
// 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<User | null> {
|
|
return this.userRepository.findOne({ phone });
|
|
}
|
|
|
|
async findById(id: string): Promise<User | null> {
|
|
return this.userRepository.findOne({ id });
|
|
}
|
|
|
|
async create(phone: string): Promise<User> {
|
|
const _raw = {
|
|
phone,
|
|
firstName: phone,
|
|
birthDate: undefined,
|
|
marriageDate: undefined,
|
|
wallet: 0,
|
|
points: 0,
|
|
};
|
|
|
|
const createData = _raw as unknown as RequiredEntityData<User>;
|
|
|
|
const user = this.userRepository.create(createData);
|
|
await this.em.persistAndFlush(user);
|
|
return user;
|
|
}
|
|
|
|
async updateUser(userId: string, dto: UpdateUserDto): Promise<User> {
|
|
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<User> = { ...dto } as Partial<User>;
|
|
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<PaginatedResult<User>> {
|
|
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<User> = {};
|
|
|
|
// 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,
|
|
},
|
|
};
|
|
}
|
|
}
|