first commit

This commit is contained in:
Saufi
2026-06-24 20:32:14 +08:00
commit 10fb30ad69
201 changed files with 21356 additions and 0 deletions

282
README.md Normal file
View File

@@ -0,0 +1,282 @@
# Sistem Pemantauan Kemampanan Pusat Data MBIP
Aplikasi web untuk **Majlis Bandaraya Iskandar Puteri (MBIP)** bagi memantau kemampanan dan pelaporan karbon pusat data dalam kawasan pentadbiran MBIP. Sistem ini membantu JPP memantau penggunaan karbon, menyemak laporan perunding, merancang program/projek pengurangan karbon, dan menguruskan pelaporan pembaharuan lesen tahunan.
Dibina dengan **Laravel 13 + MySQL + Blade + Bootstrap 5 + jQuery (Vite)**.
---
## Kandungan
- [Ciri Utama](#ciri-utama)
- [Keperluan Sistem](#keperluan-sistem)
- [Pemasangan](#pemasangan)
- [Akaun Contoh](#akaun-contoh)
- [Peraturan Perniagaan Penting](#peraturan-perniagaan-penting)
- [Senarai Semak Dinamik & Enjin Formula](#senarai-semak-dinamik--enjin-formula)
- [Peranan & Kebenaran](#peranan--kebenaran)
- [Keselamatan & Jejak Audit](#keselamatan--jejak-audit)
- [Ujian](#ujian)
- [Struktur Pangkalan Data](#struktur-pangkalan-data)
---
## Ciri Utama
1. **Autentikasi & Kawalan Akses** — log masuk/keluar, papan pemuka mengikut peranan, polisi & kebenaran, jejak audit penuh.
2. **Pengurusan Perunding** — JPP mendaftar akaun perunding; satu perunding boleh urus banyak pusat data.
3. **Pengurusan Pusat Data** — maklumat pusat data, perunding semasa, sejarah penugasan.
4. **Kitaran Pelaporan / Pembaharuan Lesen** — tahun pelaporan dikira automatik, tarikh tutup boleh dikonfigur.
5. **Pembina Templat Senarai Semak Dinamik** — JPP boleh tambah/ubah/buang seksyen, skop, item & formula **tanpa perubahan kod**.
6. **Modul Serahan Perunding** — isi senarai semak, muat naik PDF, simpan draf, hantar muktamad, jawab pembetulan.
7. **Muat Naik Dokumen & Versi** — PDF sahaja, hash fail, sejarah versi, muat turun dilindungi polisi.
8. **Aliran Kerja Semakan JPP** — tukar status, tugaskan pegawai, kunci/buka kunci, salinan keras, permohonan pembetulan.
9. **Modul Komen/Perbualan** — perbualan setiap serahan, mesej dalaman/luaran, soft delete dengan rekod audit.
10. **Senarai Peringatan** — pusat data yang belum serah untuk kitaran aktif, sorotan lampau tempoh.
11. **Papan Pemuka & Laporan** — statistik karbon A/B/C/D/E mengikut tahun, trend, pecahan status.
12. **Tetapan & Log Audit** — konfigurasi tarikh tutup lalai, had muat naik, toggle peringatan emel.
---
## Keperluan Sistem
- PHP >= 8.3 (diuji pada PHP 8.5)
- Composer 2.x
- MySQL 8.x
- Node.js >= 18 & NPM (untuk binaan aset Vite)
---
## Pemasangan
```bash
# 1. Pasang kebergantungan PHP
composer install
# 2. Pasang & bina aset frontend (Bootstrap 5 + jQuery via Vite)
npm install
npm run build # atau `npm run dev` semasa pembangunan
# 3. Fail persekitaran
cp .env.example .env
php artisan key:generate
```
Kemas kini tetapan pangkalan data dalam `.env`:
```dotenv
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mbip_datacenter
DB_USERNAME=root
DB_PASSWORD=1234
```
```bash
# 4. Cipta pangkalan data (jika belum)
# CREATE DATABASE mbip_datacenter CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 5. Migrasi + semai data (peranan, pengguna contoh, templat senarai semak, data demo)
php artisan migrate:fresh --seed
# 6. Pautan storan (laporan PDF disimpan di storage/app/private)
php artisan storage:link
# 7. Jalankan pelayan
php artisan serve
```
Lawati `http://127.0.0.1:8000` dan log masuk.
> **Nota muat naik fail:** Laporan PDF disimpan pada disk `local` (`storage/app/private/reports/…`), **di luar** direktori awam. Ia hanya boleh dimuat turun melalui laluan terlindung yang dikuatkuasakan oleh polisi.
---
## Akaun Contoh
Semua kata laluan: **`password`**
| Peranan | Emel |
|---|---|
| Super Admin | superadmin@mbip.gov.my |
| JPP Admin | jppadmin@mbip.gov.my |
| Pegawai JPP | pegawai@mbip.gov.my |
| Penolong Pegawai | penolong@mbip.gov.my |
| Kerani | kerani@mbip.gov.my |
| Perunding | perunding@example.com |
---
## Peraturan Perniagaan Penting
1. Pembaharuan lesen dibuat **pada/sebelum 1 Disember** setiap tahun (tarikh tutup boleh dikonfigur).
2. Tempoh lesen ialah **1 tahun**.
3. Perunding mesti serahkan bacaan/laporan karbon **JanuariDisember** bagi tahun pelaporan.
4. **Tahun pelaporan = Tahun pembaharuan 2.** Contoh: pembaharuan **2028** ⇒ data pelaporan **2026**.
5. JPP boleh konfigur tarikh tutup tahunan (Tetapan → Kitaran).
6. Perunding mesti isi borang senarai semak **dan** muat naik laporan PDF sebelum pembaharuan diteruskan.
7. Serahan pertama diperlukan sebelum CCC diperoleh; serahan tahunan selepas itu.
---
## Senarai Semak Dinamik & Enjin Formula
Senarai semak **tidak ditetapkan dalam kod**. Struktur disimpan secara relasi:
```
checklist_templates -> checklist_sections -> checklist_scopes -> checklist_items
-> checklist_formulas (A = SKOP1 + SKOP2 + SKOP3, dsb.)
```
Setiap item boleh diberi `formula_token` (cth: `SKOP1`, `EE`, `RE`, `D4I`). Formula seksyen merujuk token ini dan token hasil seksyen terdahulu, dinilai mengikut turutan:
| Token | Formula | Maksud |
|---|---|---|
| A | `SKOP1 + SKOP2 + SKOP3` | Jumlah pelepasan karbon |
| B | `EE + RE` | Mekanisme pengurangan |
| C | `A - B` | Baki karbon |
| D | `A - D4I - D4II` | Bersih karbon |
| E | `D / A * 100` | % pengurangan (dengan perlindungan bahagi sifar) |
Formula dinilai oleh `App\Services\ExpressionEvaluator` — sebuah **parser aritmetik selamat** (recursive descent, menyokong `+ - * / ( )` dan pemboleh ubah token). Ia **tidak** menggunakan `eval()`, jadi formula yang dikonfigur pengguna JPP adalah selamat. Hasil disimpan dalam `submission_results` untuk pengagregatan papan pemuka.
JPP boleh ubah semua ini melalui **Pembina Templat Senarai Semak** (menu sisi → Templat Senarai Semak → Bina).
---
## Peranan & Kebenaran
Menggunakan `spatie/laravel-permission`. Super Admin memintas semua semakan melalui `Gate::before`.
| Kebenaran | JPP Admin | Pegawai JPP | Penolong | Kerani | Perunding |
|---|:--:|:--:|:--:|:--:|:--:|
| Urus perunding | ✔ | ✔ | | | |
| Urus pusat data | ✔ | ✔ | ✔ | | |
| Urus penugasan | ✔ | ✔ | | | |
| Urus kitaran | ✔ | ✔ | | | |
| Urus templat senarai semak | ✔ | | | | |
| Lihat semua serahan | ✔ | ✔ | ✔ | ✔ | |
| Semak serahan | ✔ | ✔ | ✔ | | |
| Kunci/buka kunci | ✔ | ✔ | | | |
| Tanda salinan keras | ✔ | ✔ | ✔ | ✔ | |
| Urus serahan sendiri | | | | | ✔ |
| Laporan & statistik | ✔ | ✔ | ✔ | | |
| Senarai peringatan | ✔ | ✔ | ✔ | ✔ | |
| Urus pengguna & tetapan | ✔ | | | | |
Perunding hanya boleh melihat pusat data & serahan yang **ditugaskan kepadanya** (dikuatkuasakan oleh `SubmissionPolicy` & `DataCentrePolicy`).
---
## Keselamatan & Jejak Audit
- Validasi muat naik **PDF sahaja** (`mimes:pdf` + `mimetypes:application/pdf`) dengan had saiz boleh dikonfigur.
- Fail disimpan di luar laluan awam; muat turun melalui laluan terlindung + polisi.
- Setiap paparan/muat turun/tindakan diberi kuasa melalui polisi.
- Jejak audit (`spatie/laravel-activitylog`) bagi: log masuk, perubahan status, kunci/buka kunci, muat naik dokumen, permohonan pembetulan, penugasan perunding, padam komen.
- Soft delete pada model utama; komen hanya soft delete (rekod audit dikekalkan).
- Perlindungan CSRF, pengesahan sisi pelayan, indeks pangkalan data pada kunci asing/status/kitaran/perunding/pusat data.
---
## Ujian
Ujian ciri menggunakan SQLite dalam-memori (lihat `phpunit.xml`).
```bash
php artisan test
```
Liputan ujian utama:
- Perunding tidak boleh akses pusat data perunding lain
- JPP boleh cipta perunding (+ akaun log masuk)
- JPP boleh tugaskan perunding kepada pusat data
- Hanya satu perunding aktif setiap pusat data (sejarah dikekalkan)
- Tahun pelaporan dikira betul daripada tahun pembaharuan
- Perunding boleh hantar senarai semak + PDF (tidak boleh hantar tanpa PDF)
- Perunding tidak boleh edit serahan yang dikunci
- JPP tukar status ke "pembetulan perunding" hanya dengan butiran pembetulan
- Status "selesai" memerlukan tarikh hardcopy dihantar
- Senarai peringatan mengeluarkan perunding selepas serahan
- Pengiraan formula A, B, C, D, E (+ perlindungan bahagi sifar + keutamaan operator)
---
## Struktur Pangkalan Data
`users`, `roles`/`permissions` (spatie), `consultants`, `data_centres`, `data_centre_consultant_assignments`, `reporting_cycles`, `checklist_templates`, `checklist_sections`, `checklist_scopes`, `checklist_items`, `checklist_formulas`, `submissions`, `submission_answers`, `submission_results`, `submission_documents`, `submission_status_histories`, `submission_correction_requests`, `submission_comments`, `app_settings`, `activity_log`.
---
## Pengezanan Docker (Produksi Ubuntu)
Setup Docker mengikut konvensyen projek MBIP yang terbukti (rujukan: `spr2026`):
**PHP 8.4 FPM + Nginx 1.27 (Alpine) + Supervisor**, menyambung ke **MySQL luaran** pada host/LAN.
### Fail berkaitan
```
Dockerfile # imej PHP 8.4 FPM (Debian Bookworm)
docker-compose.yml # servis app + nginx, volume, network
docker-compose.override.yml # override pembangunan (auto-dimuat)
.dockerignore
.env.docker # templat env produksi
deploy.sh # skrip deploy/kemas kini
docker/
├── entrypoint.sh # tunggu MySQL, migrate, seed, cache
├── nginx/{nginx.conf,default.conf}
├── php/{php.ini,www.conf,opcache-dev.ini}
└── supervisor/supervisord.conf # php-fpm + queue worker + scheduler
```
### Langkah deploy pertama (di server Ubuntu)
```bash
# 1. Klon kod & masuk direktori
git clone <repo> mbip-datacenter && cd mbip-datacenter
# 2. Sediakan env produksi
cp .env.docker .env
php artisan key:generate --show # salin nilai ke APP_KEY dalam .env
nano .env # isi APP_URL, DB_USERNAME, DB_PASSWORD, DB_HOST
# 3. Pastikan pangkalan data MySQL wujud (pada host/LAN)
# CREATE DATABASE mbip_datacenter CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 4. Deploy (bina imej + aset + naik container + migrate + seed automatik)
chmod +x deploy.sh
./deploy.sh --build
```
Container `app` (PHP-FPM) hanya didedahkan kepada `nginx` dalaman; Nginx didedahkan pada
`NGINX_HTTP_PORT` (lalai **8010**). Letakkan reverse proxy (Nginx/Apache host atau Cloudflare)
untuk TLS/443 di hadapan port ini.
### Kemas kini (deploy seterusnya)
```bash
./deploy.sh # git pull + npm build + up + migrate + optimize
./deploy.sh --build # paksa bina semula imej (selepas tukar Dockerfile)
./deploy.sh --fresh # migrate:fresh --seed (AMARAN: kosongkan data)
./deploy.sh --logs # ikut log container
./deploy.sh --down # hentikan container
```
### Nota
- **MySQL luaran:** `DB_HOST=host.docker.internal` menyambung ke MySQL pada host Ubuntu yang sama
(dipetakan melalui `extra_hosts: host-gateway`). Tukar kepada IP pelayan MySQL jika berasingan.
- **Aset Vite** dibina pada host oleh `deploy.sh` (`npm run build`) dan dihidang terus oleh Nginx
daripada `./public` (lekapan baca-sahaja) — tiada langkah build aset dalam imej.
- **Laporan PDF** disimpan dalam volume `mbip_dc_storage` (`storage/app/private`), kekal merentas
deploy, di luar laluan awam.
- `entrypoint.sh` automatik menunggu MySQL, jalankan migrasi, semai data kali pertama, dan bina
semula cache pada setiap permulaan container.
- Supervisor menjalankan **php-fpm + queue worker + scheduler** dalam container `app`.
---
## Binaan Aset (Build)
```bash
npm run build # binaan produksi aset (manifest dalam public/build)
```
© Majlis Bandaraya Iskandar Puteri (MBIP).