feat : penambahan rumah ibadah

This commit is contained in:
unknown
2026-06-09 13:49:51 +07:00
parent 6c1437956c
commit 03c764a9c7
4 changed files with 181 additions and 201 deletions
+82 -64
View File
@@ -1,6 +1,6 @@
const db = require('../config/database');
// Mengambil data status bantuan warga berdasarkan pencarian NIK dari halaman publik
// Mengambil data status bantuan warga beserta lokasi pangkalan ibadah
const cek_status_nik = (req, res) => {
const nik_warga = req.params.nik;
@@ -9,12 +9,20 @@ const cek_status_nik = (req, res) => {
return res.status(400).json({ error: 'Format NIK tidak valid.' });
}
const kueri_pencarian = `SELECT nik, nama_kk, status_validasi, lat, lng FROM MUSTAHIK WHERE nik = ?`;
// Baris kompleks: Operasi LEFT JOIN menghubungkan tabel MUSTAHIK dengan RUMAH_IBADAH berdasarkan Foreign Key id_rumah_ibadah untuk menarik koordinat pangkalan distribusi secara simultan dalam satu siklus I/O.
const kueri_pencarian = `
SELECT m.nik, m.nama_kk, m.status_validasi, m.lat, m.lng, m.agama,
r.nama_tempat AS rumah_ibadah_nama,
r.lat AS rumah_ibadah_lat,
r.lng AS rumah_ibadah_lng
FROM MUSTAHIK m
LEFT JOIN RUMAH_IBADAH r ON m.id_rumah_ibadah = r.id
WHERE m.nik = ?
`;
// Baris kompleks: Penggunaan parameter '?' (parameterized query) memastikan bahwa mesin SQLite menganggap variabel 'nik_warga' murni sebagai nilai data, bukan sebagai perintah yang bisa dieksekusi, sehingga secara absolut mencegah serangan SQL Injection.
db.get(kueri_pencarian, [nik_warga], (error_sistem, baris_data) => {
if (error_sistem) {
console.error('Sistem gagal membaca tabel:', error_sistem.message);
console.error('Sistem gagal membaca tabel gabungan:', error_sistem.message);
return res.status(500).json({ error: 'Terjadi kegagalan server internal.' });
}
@@ -26,7 +34,7 @@ const cek_status_nik = (req, res) => {
});
};
// Mengontrol alur penerimaan data survei lapangan secara transaksional
// Mengontrol alur penerimaan data survei lapangan beserta penentuan pangkalan bantuan
const submit_survey_data = async (req, res) => {
try {
const {
@@ -35,12 +43,13 @@ const submit_survey_data = async (req, res) => {
no_kk,
jumlah_tanggungan,
lat,
lng,
lng,
agama,
indikator_bps
} = req.body;
// Praktik keamanan: Validasi input dasar untuk mencegah insersi data kosong (null pointer exception)
if (!nik || !nama_kk || !lat || !lng || !indikator_bps) {
if (!nik || !nama_kk || !lat || !lng || !agama || !indikator_bps) {
return res.status(400).json({ error: 'Payload data survei tidak lengkap dari klien.' });
}
@@ -48,65 +57,79 @@ const submit_survey_data = async (req, res) => {
db.serialize(() => {
db.run('BEGIN TRANSACTION');
// 1. Menyimpan data identitas ke tabel MUSTAHIK
const kueri_mustahik = `INSERT INTO MUSTAHIK (nik, nama_kk, no_kk, jumlah_tanggungan, lat, lng) VALUES (?, ?, ?, ?, ?, ?)`;
// Baris kompleks: Memanfaatkan db.serialize untuk memastikan kueri SELECT pencarian pangkalan ibadah selesai dieksekusi sebelum beralih ke perintah INSERT transaksional berikutnya guna menghindari balapan data (race condition).
const kueri_cari_pangkalan = `SELECT id FROM RUMAH_IBADAH WHERE agama = ? LIMIT 1`;
db.run(kueri_mustahik, [nik, nama_kk, no_kk, jumlah_tanggungan, lat, lng], function(error_mustahik) {
// Baris kompleks: Jika terjadi kegagalan pada tahap pertama ini, perintah ROLLBACK membatalkan status transaksi secara keseluruhan, mencegah adanya data terisolasi di memori.
if (error_mustahik) {
db.get(kueri_cari_pangkalan, [agama], (error_cari, baris_pangkalan) => {
if (error_cari) {
db.run('ROLLBACK');
console.error('Gagal menyimpan DML MUSTAHIK:', error_mustahik.message);
return res.status(500).json({ error: 'Gagal menyimpan data identitas warga, NIK mungkin sudah terdaftar.' });
console.error('Gagal mencocokkan rumah ibadah:', error_cari.message);
return res.status(500).json({ error: 'Gagal memproses penentuan lokasi bantuan.' });
}
// Mengekstrak ID yang otomatis digenerate (Auto Increment) untuk digunakan sebagai Foreign Key
const id_mustahik_baru = this.lastID;
const {
luas_lantai_per_kapita,
material_lantai,
material_dinding,
fasilitas_sanitasi,
sumber_air_minum,
sumber_penerangan,
pekerjaan_utama,
pendapatan_bulanan,
frekuensi_makan,
kepemilikan_aset
} = indikator_bps;
// 2. Menyimpan 10 parameter kondisi ke tabel INDIKATOR_BPS
const kueri_indikator = `
INSERT INTO INDIKATOR_BPS (
id_mustahik, luas_lantai_per_kapita, material_lantai, material_dinding,
fasilitas_sanitasi, sumber_air_minum, sumber_penerangan,
pekerjaan_utama, pendapatan_bulanan, frekuensi_makan, kepemilikan_aset
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const id_pangkalan_terpilih = baris_pangkalan ? baris_pangkalan.id : null;
db.run(kueri_indikator, [
id_mustahik_baru,
luas_lantai_per_kapita,
material_lantai,
material_dinding,
fasilitas_sanitasi,
sumber_air_minum,
sumber_penerangan,
pekerjaan_utama,
pendapatan_bulanan,
frekuensi_makan,
kepemilikan_aset
], function(error_indikator) {
if (error_indikator) {
// 1. Menyimpan data identitas dan relasi pangkalan ke tabel MUSTAHIK
const kueri_mustahik = `
INSERT INTO MUSTAHIK (nik, nama_kk, no_kk, jumlah_tanggungan, lat, lng, agama, id_rumah_ibadah)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`;
db.run(kueri_mustahik, [nik, nama_kk, no_kk, jumlah_tanggungan, lat, lng, agama, id_pangkalan_terpilih], function(error_mustahik) {
if (error_mustahik) {
db.run('ROLLBACK');
console.error('Gagal menyimpan DML INDIKATOR_BPS:', error_indikator.message);
return res.status(500).json({ error: 'Gagal merekam indikator ekonomi BPS.' });
console.error('Gagal menyimpan DML MUSTAHIK:', error_mustahik.message);
return res.status(500).json({ error: 'Gagal menyimpan data identitas warga, NIK mungkin sudah terdaftar.' });
}
// Secara permanen menerapkan semua kueri yang tertunda ke dalam file SQLite
db.run('COMMIT');
res.status(201).json({
message: 'Data survei kriteria BPS berhasil disubmit',
id_mustahik: id_mustahik_baru
const id_mustahik_baru = this.lastID;
const {
luas_lantai_per_kapita,
material_lantai,
material_dinding,
fasilitas_sanitasi,
sumber_air_minum,
sumber_penerangan,
pekerjaan_utama,
pendapatan_bulanan,
frekuensi_makan,
kepemilikan_aset
} = indikator_bps;
// 2. Menyimpan 10 parameter kondisi ke tabel INDIKATOR_BPS
const kueri_indikator = `
INSERT INTO INDIKATOR_BPS (
id_mustahik, luas_lantai_per_kapita, material_lantai, material_dinding,
fasilitas_sanitasi, sumber_air_minum, sumber_penerangan,
pekerjaan_utama, pendapatan_bulanan, frekuensi_makan, kepemilikan_aset
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
db.run(kueri_indikator, [
id_mustahik_baru,
luas_lantai_per_kapita,
material_lantai,
material_dinding,
fasilitas_sanitasi,
sumber_air_minum,
sumber_penerangan,
pekerjaan_utama,
pendapatan_bulanan,
frekuensi_makan,
kepemilikan_aset
], function(error_indikator) {
if (error_indikator) {
db.run('ROLLBACK');
console.error('Gagal menyimpan DML INDIKATOR_BPS:', error_indikator.message);
return res.status(500).json({ error: 'Gagal merekam indikator ekonomi BPS.' });
}
// Secara permanen menerapkan semua kueri yang tertunda ke dalam file SQLite
db.run('COMMIT');
res.status(201).json({
message: 'Data survei kriteria BPS dan pangkalan bantuan berhasil disimpan.',
id_mustahik: id_mustahik_baru
});
});
});
});
@@ -120,10 +143,6 @@ const submit_survey_data = async (req, res) => {
// Menarik agregasi data statistik untuk ditampilkan di halaman publik
const ambil_statistik_publik = (req, res) => {
/* Baris kompleks: Kueri ini menggunakan fungsi agregasi COUNT dikombinasikan dengan ekspresi
kondisional CASE WHEN. Pendekatan ini sangat optimal karena mesin SQLite hanya perlu
memindai tabel MUSTAHIK satu kali untuk mendapatkan dua nilai hitungan yang berbeda,
sehingga menghemat alokasi memori dan waktu eksekusi server backend. */
const kueri_statistik = `
SELECT
COUNT(CASE WHEN status_validasi = 'Verified' THEN 1 END) AS terverifikasi,
@@ -140,9 +159,8 @@ const ambil_statistik_publik = (req, res) => {
});
};
// Mengekspor semua fungsi agar dapat diakses oleh file router
module.exports = {
cek_status_nik,
submit_survey_data,
ambil_statistik_publik
};
};