hospital-log/backend/api/src/modules/rekammedis/rekammedis.service.ts

401 lines
10 KiB
TypeScript
Raw Normal View History

import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { Prisma, rekam_medis } from '@dist/generated/prisma';
import { CreateRekamMedisDto } from './dto/create-rekammedis.dto';
2025-11-06 07:10:04 +00:00
import { LogService } from '../log/log.service';
import { sha256 } from '@api/common/crypto/hash';
import { ActiveUserPayload } from '../auth/decorator/current-user.decorator';
import { PayloadRekamMedisDto } from './dto/payload-rekammedis.dto';
// import { CreateLogDto } from '../log/dto/create-log.dto';
@Injectable()
export class RekammedisService {
private readonly KNOWN_BLOOD_TYPES = ['A', 'B', 'AB', 'O'];
private readonly KNOWN_TINDAK_LANJUT = [
'Dipulangkan untuk Kontrol',
'Dirawat',
'Dirujuk ke RS',
'Konsul Ke Poli Lain',
'Konsultasi Dokter Spesialis',
'Kontrol',
'Kontrol Ulang',
'Masuk Rawat Inap',
'Meninggal Dunia Sebelum Dirawat',
'Meninggal Dunia Setelah Dirawat',
'Pulang',
'Rencana Operasi',
'Rujuk Balik',
'Selesai Pelayanan IGD',
'Selesai Pelayanan Rawat Jalan',
];
2025-11-06 07:10:04 +00:00
constructor(
private prisma: PrismaService,
private log: LogService,
) {}
createHashingPayload(currentData: PayloadRekamMedisDto): string {
return sha256(JSON.stringify(currentData));
}
determineStatus(rawFabricLog: any, index: number, arrLength: number): any {
const flatLog = {
...rawFabricLog.value,
txId: rawFabricLog.txId,
timestamp: rawFabricLog.value.timestamp,
};
console.log('Processed flat log:', flatLog);
if (
index === arrLength - 1 &&
rawFabricLog.value.event === 'rekam_medis_created'
) {
flatLog.status = 'ORIGINAL';
} else {
flatLog.status = 'UPDATED';
}
return flatLog;
}
async getAllRekamMedis(params: {
take?: number;
skip?: number;
page?: number;
orderBy?: any;
no_rm?: string;
order?: 'asc' | 'desc';
id_visit?: string;
nama_pasien?: string;
tanggal_start?: string;
tanggal_end?: string;
umur_min?: string;
umur_max?: string;
jenis_kelamin?: string;
gol_darah?: string;
kode_diagnosa?: string;
tindak_lanjut?: string;
}) {
const {
skip,
page,
orderBy,
order,
no_rm,
id_visit,
nama_pasien,
tanggal_start,
tanggal_end,
umur_min,
umur_max,
kode_diagnosa,
} = params;
const golDarahArray = params.gol_darah?.split(',') || [];
const tindakLanjutArray = params.tindak_lanjut?.split(',') || [];
2025-11-06 07:10:04 +00:00
const jkCharacter =
params.jenis_kelamin == 'perempuan'
? 'P'
: params.jenis_kelamin == 'laki-laki'
? 'L'
: '';
const take = params.take ? parseInt(params.take.toString()) : 10;
const skipValue = skip
? parseInt(skip.toString())
: page
? (parseInt(page.toString()) - 1) * take
: 0;
const buildMultiSelectFilter = (
fieldName: string,
selectedValues: string[],
knownValues: string[],
unknownLabel: string,
includesDash: boolean = false,
) => {
if (selectedValues.length === 0) {
return undefined;
}
const hasKnownValues = selectedValues.some((val) =>
knownValues.includes(val),
);
const hasUnknown = selectedValues.includes(unknownLabel);
const totalOptions = knownValues.length + 1;
if (selectedValues.length === totalOptions) {
return undefined;
}
if (hasUnknown && !hasKnownValues) {
const conditions: any[] = [{ [fieldName]: { equals: null } }];
if (includesDash) {
conditions.push({ [fieldName]: { equals: '-' } });
conditions.push({ [fieldName]: { notIn: knownValues } });
}
return conditions.length > 1 ? { OR: conditions } : conditions[0];
}
if (hasKnownValues && hasUnknown) {
const knownSelected = selectedValues.filter(
(val) => val !== unknownLabel,
);
const conditions: any[] = [
{ [fieldName]: { in: knownSelected } },
{ [fieldName]: { equals: null } },
];
if (includesDash) {
conditions.push({ [fieldName]: { equals: '-' } });
conditions.push({ [fieldName]: { notIn: knownValues } });
}
return { OR: conditions };
}
return { [fieldName]: { in: selectedValues } };
};
const golDarahFilter = buildMultiSelectFilter(
'gol_darah',
golDarahArray,
this.KNOWN_BLOOD_TYPES,
'Tidak Tahu',
true,
);
const tindakLanjutFilter = buildMultiSelectFilter(
'tindak_lanjut',
tindakLanjutArray,
this.KNOWN_TINDAK_LANJUT,
'Belum Ada Keterangan',
false,
);
const whereClause = {
no_rm: no_rm ? { startsWith: no_rm } : undefined,
id_visit: id_visit ? { contains: id_visit } : undefined,
nama_pasien: nama_pasien ? { contains: nama_pasien } : undefined,
waktu_visit:
tanggal_start && tanggal_end
? {
gte: new Date(tanggal_start),
lte: new Date(tanggal_end),
}
: undefined,
umur:
umur_min && umur_max
? {
gte: parseInt(umur_min, 10),
lte: parseInt(umur_max, 10),
}
: undefined,
2025-11-06 07:10:04 +00:00
jenis_kelamin: jkCharacter ? { equals: jkCharacter } : undefined,
kode_diagnosa: kode_diagnosa ? { contains: kode_diagnosa } : undefined,
...golDarahFilter,
...tindakLanjutFilter,
};
const results = await this.prisma.rekam_medis.findMany({
skip: skipValue,
take: take,
where: whereClause,
orderBy: orderBy
2025-11-06 07:10:04 +00:00
? { [orderBy]: order || 'desc' }
: { waktu_visit: order ? order : 'asc' },
});
const count = await this.prisma.rekam_medis.count({
where: whereClause,
});
const umurMin = await this.prisma.rekam_medis.findMany({
distinct: ['umur'],
orderBy: {
umur: 'asc',
},
select: {
umur: true,
},
});
const umurMax = await this.prisma.rekam_medis.findMany({
distinct: ['umur'],
orderBy: {
umur: 'desc',
},
select: {
umur: true,
},
});
const rangeUmur = {
min: umurMin.length > 0 ? umurMin[0].umur : null,
max: umurMax.length > 0 ? umurMax[0].umur : null,
};
return {
...results,
totalCount: count,
rangeUmur: rangeUmur,
};
}
2025-11-06 07:10:04 +00:00
async getRekamMedisById(id_visit: string) {
return this.prisma.rekam_medis.findUnique({
where: { id_visit },
});
}
async createRekamMedis(data: CreateRekamMedisDto, user: ActiveUserPayload) {
const latestId = await this.prisma.rekam_medis.findFirst({
orderBy: { waktu_visit: 'desc' },
});
let newId = '';
let xCounter = 0;
let rekamMedis: Prisma.rekam_medisCreateInput;
for (let i = (latestId?.id_visit?.length ?? 0) - 1; i >= 0; i--) {
if (latestId?.id_visit[i] === 'X') {
xCounter++;
} else {
newId = latestId?.id_visit?.substring(0, i + 1) || '';
break;
}
}
if (xCounter < 1) {
newId = (parseInt(latestId?.id_visit || '0', 10) + 1).toString();
} else {
newId = (parseInt(newId || '0', 10) + 1).toString();
}
rekamMedis = {
...data,
id_visit: newId,
waktu_visit: new Date(),
};
2025-11-06 07:10:04 +00:00
const logData = {
event: 'rekam_medis_created',
payload: {
dokter_id: 123,
visit_id: newId,
anamnese: data.anamnese,
jenis_kasus: data.jenis_kasus,
tindak_lanjut: data.tindak_lanjut,
},
};
try {
const newRekamMedis = await this.prisma.$transaction(async (tx) => {
const createdRekamMedis = await tx.rekam_medis.create({
data: rekamMedis,
});
2025-11-06 07:10:04 +00:00
const logPayload = JSON.stringify(logData.payload);
const payloadHash = sha256(logPayload);
const data = {
id: `REKAM_${newId}`,
event: 'rekam_medis_created',
user_id: user.sub,
payload: payloadHash,
};
const createdLog = await this.log.storeLog(data);
return {
...createdRekamMedis,
log: createdLog,
};
});
return newRekamMedis;
} catch (error) {
console.error('Error creating Rekam Medis:', error);
throw error;
}
}
2025-11-06 07:10:04 +00:00
async getRekamMedisLogById(id_visit: string) {
const idLog = `REKAM_${id_visit}`;
const rawLogs = await this.log.getLogById(idLog);
const currentData = await this.getRekamMedisById(id_visit);
if (!currentData) {
throw new Error(`Rekam Medis with id_visit ${id_visit} not found`);
}
const currentDataHash = this.createHashingPayload({
dokter_id: 123,
visit_id: currentData.id_visit,
anamnese: currentData.anamnese ?? '',
jenis_kasus: currentData.jenis_kasus ?? '',
tindak_lanjut: currentData.tindak_lanjut ?? '',
});
const latestPayload = rawLogs[0].value.payload;
const isTampered = currentDataHash !== latestPayload;
const chronologicalLogs = [...rawLogs];
const processedLogs = chronologicalLogs.map((log, index) => {
return this.determineStatus(log, index, chronologicalLogs.length);
});
return {
logs: processedLogs,
isTampered: isTampered,
currentDataHash: currentDataHash,
};
}
async updateRekamMedis(
id_visit: string,
data: CreateRekamMedisDto,
user: ActiveUserPayload,
) {
const rekamMedis = await this.prisma.rekam_medis.update({
where: { id_visit },
data: {
...data,
},
});
const logData = {
event: 'rekam_medis_updated',
payload: {
dokter_id: 123,
visit_id: id_visit,
anamnese: data.anamnese,
jenis_kasus: data.jenis_kasus,
tindak_lanjut: data.tindak_lanjut,
},
};
const logPayload = JSON.stringify(logData.payload);
const payloadHash = sha256(logPayload);
const logDto = {
id: `REKAM_${id_visit}`,
event: 'rekam_medis_updated',
user_id: user.sub,
payload: payloadHash,
};
const createdLog = await this.log.storeLog(logDto);
return {
...rekamMedis,
log: createdLog,
};
}
2025-11-06 10:07:08 +00:00
async countRekamMedis() {
return this.prisma.rekam_medis.count();
}
}