Files
E-Vote/app/Http/Controllers/API/v1/Voter/ImportKeanggotaanJsonController.php
T

177 lines
5.4 KiB
PHP

<?php
namespace App\Http\Controllers\API\v1\Voter;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Util;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
/**
* Import keanggotaan JSON (match by no_anggota) into voter money columns for the current election.
* Expected row keys: no_anggota, pelaburan, pelaburan_2, saham, yuran. Null/empty → 0.
*/
class ImportKeanggotaanJsonController extends Controller
{
private const UPDATABLE_FIELDS = [
'pelaburan',
'pelaburan_2',
'saham',
'yuran',
];
public function __invoke(Request $request)
{
$this->validate($request, [
'file' => 'required|file|max:10240',
]);
$file = $request->file('file');
$extension = strtolower($file->getClientOriginalExtension() ?: '');
if ($extension !== 'json') {
return response()->json([
'status' => 'failed',
'message' => 'Fail mestilah format .json.',
], 422);
}
$raw = file_get_contents($file->getRealPath());
$rows = json_decode($raw, true);
if (!is_array($rows)) {
return response()->json([
'status' => 'failed',
'message' => 'JSON tidak sah. Jangkaan array objek.',
], 422);
}
$map = [];
foreach ($rows as $row) {
if (!is_array($row)) {
continue;
}
$noAnggota = isset($row['no_anggota']) ? trim((string) $row['no_anggota']) : '';
if ($noAnggota === '') {
continue;
}
$payload = [];
foreach (self::UPDATABLE_FIELDS as $field) {
if (array_key_exists($field, $row)) {
$payload[$field] = $this->cleanNumber($row[$field]);
}
}
if (count($payload) === 0) {
continue;
}
$map[$noAnggota] = $payload;
}
if (count($map) === 0) {
return response()->json([
'status' => 'failed',
'message' => 'Tiada baris sah dalam JSON (perlu no_anggota dan sekurang-kurangnya satu medan).',
], 422);
}
$electionId = (int) Util::getCurrentElection();
if ($electionId <= 0) {
return response()->json([
'status' => 'failed',
'message' => 'Tidak dapat mengenal pasti election semasa.',
], 422);
}
$stats = [
'election_id' => $electionId,
'json_rows' => count($map),
'matched' => 0,
'updated' => 0,
'unchanged' => 0,
'missing_in_voter' => 0,
'missing_in_json' => 0,
];
$matchedKeys = [];
DB::table('voter')
->select(array_merge(['id', 'no_anggota'], self::UPDATABLE_FIELDS))
->where('election_id', $electionId)
->orderBy('id')
->chunkById(500, function ($chunk) use ($map, &$stats, &$matchedKeys) {
foreach ($chunk as $voter) {
$noAnggota = $voter->no_anggota !== null ? trim((string) $voter->no_anggota) : '';
if ($noAnggota === '' || !array_key_exists($noAnggota, $map)) {
$stats['missing_in_json']++;
continue;
}
$stats['matched']++;
$matchedKeys[$noAnggota] = true;
$jsonRow = $map[$noAnggota];
$update = [];
foreach ($jsonRow as $field => $newValue) {
$oldValue = $voter->{$field} !== null ? (float) $voter->{$field} : 0.0;
if (abs($oldValue - $newValue) >= 0.00001) {
$update[$field] = $newValue;
}
}
if (count($update) === 0) {
$stats['unchanged']++;
continue;
}
$update['updated_at'] = now();
DB::table('voter')->where('id', $voter->id)->update($update);
$stats['updated']++;
}
}, 'id');
foreach ($map as $noAnggota => $_payload) {
if (!isset($matchedKeys[$noAnggota])) {
$stats['missing_in_voter']++;
}
}
activity()
->withProperties([
'election_id' => $electionId,
'filename' => $file->getClientOriginalName(),
'stats' => $stats,
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
])
->log('import keanggotaan json');
return response()->json([
'status' => 'success',
'message' => 'Import keanggotaan berjaya.',
'data' => $stats,
]);
}
private function cleanNumber($value): float
{
if ($value === null || $value === '') {
return 0.0;
}
$value = (string) $value;
$value = str_replace(['RM', ',', ' '], '', $value);
if (preg_match('/^\((.*)\)$/', $value, $matches)) {
$value = $matches[1];
}
$value = preg_replace('/[^0-9\.\-]/', '', $value);
return is_numeric($value) ? (float) $value : 0.0;
}
}