const db = require('../config/database'); // Mengambil data status bantuan warga beserta lokasi pangkalan ibadah const cek_status_nik = (req, res) => { const nik_warga = req.params.nik; // Praktik keamanan: Memastikan input NIK murni berupa 16 digit angka untuk mencegah manipulasi query if (!/^\d{16}$/.test(nik_warga)) { return res.status(400).json({ error: 'Format NIK tidak valid.' }); } // 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 = ? `; db.get(kueri_pencarian, [nik_warga], (error_sistem, baris_data) => { if (error_sistem) { console.error('Sistem gagal membaca tabel gabungan:', error_sistem.message); return res.status(500).json({ error: 'Terjadi kegagalan server internal.' }); } if (!baris_data) { return res.status(404).json({ error: 'Data tidak ditemukan.' }); } res.status(200).json(baris_data); }); }; // Mengontrol alur penerimaan data survei lapangan beserta penentuan pangkalan bantuan const submit_survey_data = async (req, res) => { try { const { nik, nama_kk, no_kk, jumlah_tanggungan, lat, 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 || !agama || !indikator_bps) { return res.status(400).json({ error: 'Payload data survei tidak lengkap dari klien.' }); } // Memulai transaksi basis data untuk menjaga integritas penyimpanan parsial db.serialize(() => { db.run('BEGIN TRANSACTION'); // 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.get(kueri_cari_pangkalan, [agama], (error_cari, baris_pangkalan) => { if (error_cari) { db.run('ROLLBACK'); console.error('Gagal mencocokkan rumah ibadah:', error_cari.message); return res.status(500).json({ error: 'Gagal memproses penentuan lokasi bantuan.' }); } const id_pangkalan_terpilih = baris_pangkalan ? baris_pangkalan.id : null; // 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 MUSTAHIK:', error_mustahik.message); return res.status(500).json({ error: 'Gagal menyimpan data identitas warga, NIK mungkin sudah terdaftar.' }); } 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 }); }); }); }); }); } catch (error_sistem) { console.error('Kesalahan eksekusi sistem:', error_sistem); res.status(500).json({ error: 'Kegagalan pemrosesan internal server.' }); } }; // Menarik agregasi data statistik untuk ditampilkan di halaman publik const ambil_statistik_publik = (req, res) => { const kueri_statistik = ` SELECT COUNT(CASE WHEN status_validasi = 'Verified' THEN 1 END) AS terverifikasi, COUNT(CASE WHEN status_validasi = 'Pending' THEN 1 END) AS diproses FROM MUSTAHIK `; db.get(kueri_statistik, [], (error_sql, baris_data) => { if (error_sql) { console.error('Gagal menarik agregasi statistik:', error_sql.message); return res.status(500).json({ error: 'Gagal memuat statistik.' }); } res.status(200).json(baris_data); }); }; module.exports = { cek_status_nik, submit_survey_data, ambil_statistik_publik };