Files
pos-system/services/iam-service/src/modules/common/repository.ts

221 lines
8.5 KiB
TypeScript

import { logger } from '@goodgo/logger';
import { PrismaClient } from '@prisma/client';
import { DatabaseError } from '../../errors/http-error';
import { formatErrorForLogging, hasCode } from '../../utils/error-utils';
/**
* EN: Base repository class providing common database operations
* VI: Base repository class cung cấp các thao tác database chung
*/
export abstract class BaseRepository<T, CreateInput, UpdateInput> {
protected prisma: PrismaClient;
protected modelName: string;
constructor(prisma: PrismaClient, modelName: string) {
this.prisma = prisma;
this.modelName = modelName;
}
/**
* EN: Find entity by ID
* VI: Tìm entity theo ID
*/
async findById(id: string): Promise<T | null> {
try {
logger.debug(`Finding ${this.modelName} by ID / Tìm ${this.modelName} theo ID`, { id });
const entity = await (this.prisma as any)[this.modelName].findUnique({
where: { id },
});
logger.debug(`${this.modelName} ${entity ? 'found' : 'not found'} / ${this.modelName} ${entity ? 'đã tìm thấy' : 'không tìm thấy'}`, { id });
return entity;
} catch (error: unknown) {
logger.error(`Failed to find ${this.modelName} by ID / Không thể tìm ${this.modelName} theo ID`, { ...formatErrorForLogging(error), id });
throw new DatabaseError(`Failed to find ${this.modelName}`, { id, originalError: error });
}
}
/**
* EN: Find entity by unique field
* VI: Tìm entity theo field duy nhất
*/
async findByUnique(field: string, value: unknown): Promise<T | null> {
try {
logger.debug(`Finding ${this.modelName} by ${field} / Tìm ${this.modelName} theo ${field}`, { field, value });
const entity = await (this.prisma as any)[this.modelName].findUnique({
where: { [field]: value },
});
logger.debug(`${this.modelName} ${entity ? 'found' : 'not found'} / ${this.modelName} ${entity ? 'đã tìm thấy' : 'không tìm thấy'}`, { field, value });
return entity;
} catch (error: unknown) {
logger.error(`Failed to find ${this.modelName} by ${field} / Không thể tìm ${this.modelName} theo ${field}`, { ...formatErrorForLogging(error), field, value });
throw new DatabaseError(`Failed to find ${this.modelName}`, { field, value, originalError: error });
}
}
/**
* EN: Find all entities with optional filtering
* VI: Tìm tất cả entities với filtering tùy chọn
*/
async findAll(options?: {
where?: any;
orderBy?: any;
skip?: number;
take?: number;
include?: any;
}): Promise<T[]> {
try {
logger.debug(`Finding all ${this.modelName} / Tìm tất cả ${this.modelName}`, options);
const entities = await (this.prisma as any)[this.modelName].findMany(options || {});
logger.debug(`Found ${entities.length} ${this.modelName} entities / Đã tìm thấy ${entities.length} ${this.modelName} entities`);
return entities;
} catch (error: unknown) {
logger.error(`Failed to find all ${this.modelName} / Không thể tìm tất cả ${this.modelName}`, { ...formatErrorForLogging(error), options });
throw new DatabaseError(`Failed to find ${this.modelName} entities`, { options, originalError: error });
}
}
/**
* EN: Create new entity
* VI: Tạo entity mới
*/
async create(data: CreateInput): Promise<T> {
try {
logger.debug(`Creating new ${this.modelName} / Tạo ${this.modelName} mới`, { data });
const entity = await (this.prisma as any)[this.modelName].create({
data,
});
logger.debug(`${this.modelName} created successfully / ${this.modelName} đã được tạo thành công`, { id: (entity as any).id });
return entity;
} catch (error: unknown) {
logger.error(`Failed to create ${this.modelName} / Không thể tạo ${this.modelName}`, { ...formatErrorForLogging(error), data });
throw new DatabaseError(`Failed to create ${this.modelName}`, { data, originalError: error });
}
}
/**
* EN: Update entity by ID
* VI: Cập nhật entity theo ID
*/
async update(id: string, data: UpdateInput): Promise<T> {
try {
logger.debug(`Updating ${this.modelName} / Cập nhật ${this.modelName}`, { id, data });
const entity = await (this.prisma as any)[this.modelName].update({
where: { id },
data,
});
logger.debug(`${this.modelName} updated successfully / ${this.modelName} đã được cập nhật thành công`, { id });
return entity;
} catch (error: unknown) {
if (hasCode(error) && error.code === 'P2025') {
logger.warn(`${this.modelName} not found for update / ${this.modelName} không tìm thấy để cập nhật`, { id });
throw new DatabaseError(`${this.modelName} not found`, { id });
}
logger.error(`Failed to update ${this.modelName} / Không thể cập nhật ${this.modelName}`, { ...formatErrorForLogging(error), id, data });
throw new DatabaseError(`Failed to update ${this.modelName}`, { id, data, originalError: error });
}
}
/**
* EN: Delete entity by ID
* VI: Xóa entity theo ID
*/
async delete(id: string): Promise<boolean> {
try {
logger.debug(`Deleting ${this.modelName} / Xóa ${this.modelName}`, { id });
await (this.prisma as any)[this.modelName].delete({
where: { id },
});
logger.debug(`${this.modelName} deleted successfully / ${this.modelName} đã được xóa thành công`, { id });
return true;
} catch (error: unknown) {
if (hasCode(error) && error.code === 'P2025') {
logger.warn(`${this.modelName} not found for deletion / ${this.modelName} không tìm thấy để xóa`, { id });
throw new DatabaseError(`${this.modelName} not found`, { id });
}
logger.error(`Failed to delete ${this.modelName} / Không thể xóa ${this.modelName}`, { ...formatErrorForLogging(error), id });
throw new DatabaseError(`Failed to delete ${this.modelName}`, { id, originalError: error });
}
}
/**
* EN: Count entities with optional filtering
* VI: Đếm entities với filtering tùy chọn
*/
async count(where?: any): Promise<number> {
try {
logger.debug(`Counting ${this.modelName} / Đếm ${this.modelName}`, { where });
const count = await (this.prisma as any)[this.modelName].count({
where,
});
logger.debug(`Counted ${count} ${this.modelName} entities / Đã đếm ${count} ${this.modelName} entities`);
return count;
} catch (error: unknown) {
logger.error(`Failed to count ${this.modelName} / Không thể đếm ${this.modelName}`, { ...formatErrorForLogging(error), where });
throw new DatabaseError(`Failed to count ${this.modelName}`, { where, originalError: error });
}
}
/**
* EN: Check if entity exists by ID
* VI: Kiểm tra entity có tồn tại theo ID
*/
async exists(id: string): Promise<boolean> {
try {
const count = await this.count({ id });
return count > 0;
} catch (error: unknown) {
logger.error(`Failed to check if ${this.modelName} exists / Không thể kiểm tra ${this.modelName} có tồn tại`, { ...formatErrorForLogging(error), id });
throw error;
}
}
/**
* EN: Execute transaction with multiple operations
* VI: Thực thi transaction với nhiều operations
*/
async transaction<R>(callback: (tx: any) => Promise<R>): Promise<R> {
try {
logger.debug(`Starting ${this.modelName} transaction / Bắt đầu transaction ${this.modelName}`);
const result = await this.prisma.$transaction(async (tx) => {
return await callback(tx);
});
logger.debug(`${this.modelName} transaction completed successfully / Transaction ${this.modelName} đã hoàn thành thành công`);
return result;
} catch (error: unknown) {
logger.error(`${this.modelName} transaction failed / Transaction ${this.modelName} thất bại`, formatErrorForLogging(error));
throw new DatabaseError(`${this.modelName} transaction failed`, { originalError: error });
}
}
}
/**
* EN: Generic repository interface for type safety
* VI: Generic repository interface để type safety
*/
export interface IRepository<T, CreateInput, UpdateInput> {
findById(id: string): Promise<T | null>;
findByUnique(field: string, value: unknown): Promise<T | null>;
findAll(options?: any): Promise<T[]>;
create(data: CreateInput): Promise<T>;
update(id: string, data: UpdateInput): Promise<T>;
delete(id: string): Promise<boolean>;
count(where?: any): Promise<number>;
exists(id: string): Promise<boolean>;
}