first commit
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"backendcareit/database"
|
||||
"backendcareit/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// SendEmail - Ngirim email pake SMTP bro
|
||||
func SendEmail(to, subject, body string) error {
|
||||
// Ambil konfigurasi dari env variable dulu, lebih aman
|
||||
from := os.Getenv("EMAIL_FROM")
|
||||
password := os.Getenv("EMAIL_PASSWORD")
|
||||
smtpHost := os.Getenv("SMTP_HOST")
|
||||
smtpPort := os.Getenv("SMTP_PORT")
|
||||
|
||||
// Kalau env variable gak ada, pake default value (biar kompatibel sama versi lama)
|
||||
if from == "" {
|
||||
from = "careit565@gmail.com"
|
||||
}
|
||||
if password == "" {
|
||||
password = "gkhz bjax uamw xydf"
|
||||
}
|
||||
if smtpHost == "" {
|
||||
smtpHost = "smtp.gmail.com"
|
||||
}
|
||||
if smtpPort == "" {
|
||||
smtpPort = "587"
|
||||
}
|
||||
|
||||
if from == "" || password == "" || smtpHost == "" || smtpPort == "" {
|
||||
return fmt.Errorf("konfigurasi email tidak lengkap. Pastikan EMAIL_FROM, EMAIL_PASSWORD, SMTP_HOST, dan SMTP_PORT sudah di-set")
|
||||
}
|
||||
|
||||
// Setup authentication
|
||||
auth := smtp.PlainAuth("", from, password, smtpHost)
|
||||
|
||||
// Format email message
|
||||
msg := []byte(fmt.Sprintf("To: %s\r\n", to) +
|
||||
fmt.Sprintf("Subject: %s\r\n", subject) +
|
||||
"MIME-Version: 1.0\r\n" +
|
||||
"Content-Type: text/html; charset=UTF-8\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n")
|
||||
|
||||
// Send email
|
||||
addr := fmt.Sprintf("%s:%s", smtpHost, smtpPort)
|
||||
err := smtp.SendMail(addr, auth, from, []string{to}, msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gagal mengirim email: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendEmailToMultiple - Ngirim email ke banyak orang sekaligus
|
||||
func SendEmailToMultiple(to []string, subject, body string) error {
|
||||
from := os.Getenv("EMAIL_FROM")
|
||||
password := os.Getenv("EMAIL_PASSWORD")
|
||||
smtpHost := os.Getenv("SMTP_HOST")
|
||||
smtpPort := os.Getenv("SMTP_PORT")
|
||||
|
||||
if from == "" {
|
||||
from = "asikmahdi@gmail.com"
|
||||
}
|
||||
if password == "" {
|
||||
password = "njom rhxb prrj tuoj"
|
||||
}
|
||||
if smtpHost == "" {
|
||||
smtpHost = "smtp.gmail.com"
|
||||
}
|
||||
if smtpPort == "" {
|
||||
smtpPort = "587"
|
||||
}
|
||||
|
||||
if from == "" || password == "" || smtpHost == "" || smtpPort == "" {
|
||||
return fmt.Errorf("konfigurasi email tidak lengkap")
|
||||
}
|
||||
|
||||
if len(to) == 0 {
|
||||
return fmt.Errorf("daftar penerima email tidak boleh kosong")
|
||||
}
|
||||
|
||||
// Setup authentication
|
||||
auth := smtp.PlainAuth("", from, password, smtpHost)
|
||||
|
||||
// Rapihin header To buat semua penerima
|
||||
toHeader := strings.Join(to, ", ")
|
||||
|
||||
// Format email message
|
||||
msg := []byte(fmt.Sprintf("To: %s\r\n", toHeader) +
|
||||
fmt.Sprintf("Subject: %s\r\n", subject) +
|
||||
"MIME-Version: 1.0\r\n" +
|
||||
"Content-Type: text/html; charset=UTF-8\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n")
|
||||
|
||||
// Kirim email ke semua orang sekaligus
|
||||
addr := fmt.Sprintf("%s:%s", smtpHost, smtpPort)
|
||||
err := smtp.SendMail(addr, auth, from, to, msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gagal mengirim email: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendEmailTest - Cuma buat test kirim email ke teman-teman
|
||||
func SendEmailTest() error {
|
||||
to := []string{"stylohype685@gmail.com", "pasaribumonica2@gmail.com", "yestondehaan607@gmail.com"}
|
||||
subject := "Test Email - Sistem Billing Care IT"
|
||||
body := `
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||
.header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }
|
||||
.content { background-color: #f9f9f9; padding: 20px; margin-top: 20px; }
|
||||
.footer { margin-top: 20px; padding: 10px; text-align: center; font-size: 12px; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h2>Test Email - Sistem Billing Care IT</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Halo!</p>
|
||||
<p>Ini adalah email test dari sistem billing Care IT.</p>
|
||||
<p>Jika Anda menerima email ini, berarti sistem email berfungsi dengan baik.</p>
|
||||
<p>Terima kasih!</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>Sistem Billing Care IT</p>
|
||||
<p>Email ini dikirim untuk keperluan testing.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
if err := SendEmailToMultiple(to, subject, body); err != nil {
|
||||
return fmt.Errorf("gagal mengirim email test: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendEmailBillingSignToDokter mengirim email ke semua dokter yang menangani pasien tentang billing sign
|
||||
func SendEmailBillingSignToDokter(idBilling int) error {
|
||||
// 1. Ambil billing berdasarkan ID_Billing
|
||||
var billing models.BillingPasien
|
||||
if err := database.DB.First(&billing, idBilling).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fmt.Errorf("billing dengan ID_Billing=%d tidak ditemukan", idBilling)
|
||||
}
|
||||
return fmt.Errorf("gagal mengambil billing: %w", err)
|
||||
}
|
||||
|
||||
// 2. Ambil semua dokter dari billing_dokter
|
||||
var dokterList []models.Dokter
|
||||
if err := database.DB.
|
||||
Table("\"billing_dokter\" bd").
|
||||
Select("d.*").
|
||||
Joins("JOIN \"dokter\" d ON bd.\"ID_Dokter\" = d.\"ID_Dokter\"").
|
||||
Where("bd.\"ID_Billing\" = ?", idBilling).
|
||||
Find(&dokterList).Error; err != nil {
|
||||
return fmt.Errorf("gagal mengambil dokter: %w", err)
|
||||
}
|
||||
|
||||
if len(dokterList) == 0 {
|
||||
return fmt.Errorf("tidak ada dokter yang terkait dengan billing ID_Billing=%d", idBilling)
|
||||
}
|
||||
|
||||
// 3. Ambil data pasien untuk informasi lengkap
|
||||
var pasien models.Pasien
|
||||
if err := database.DB.Where("\"ID_Pasien\" = ?", billing.ID_Pasien).First(&pasien).Error; err != nil {
|
||||
return fmt.Errorf("gagal mengambil data pasien: %w", err)
|
||||
}
|
||||
|
||||
// 4. Format billing sign untuk ditampilkan
|
||||
billingSignDisplay := strings.ToUpper(billing.Billing_sign)
|
||||
if billingSignDisplay == "" {
|
||||
billingSignDisplay = "Belum ditentukan"
|
||||
}
|
||||
|
||||
// Untuk pengiriman ke dokter: kirim personalisasi per dokter (salam pakai nama dokter)
|
||||
// Kumpulkan alamat per dokter dan jalankan pengiriman secara async (goroutine)
|
||||
anyEmail := false
|
||||
subject := fmt.Sprintf("Notifikasi Billing Sign - Pasien: %s", pasien.Nama_Pasien)
|
||||
|
||||
for _, dokter := range dokterList {
|
||||
// kumpulkan alamat untuk dokter ini
|
||||
addrs := make([]string, 0, 2)
|
||||
if e := strings.TrimSpace(dokter.Email_UB); e != "" {
|
||||
addrs = append(addrs, e)
|
||||
}
|
||||
if e := strings.TrimSpace(dokter.Email_Pribadi); e != "" {
|
||||
// hindari duplikat antara UB dan pribadi
|
||||
if len(addrs) == 0 || addrs[0] != e {
|
||||
addrs = append(addrs, e)
|
||||
}
|
||||
}
|
||||
|
||||
if len(addrs) == 0 {
|
||||
continue
|
||||
}
|
||||
anyEmail = true
|
||||
|
||||
// buat body yang dipersonalisasi untuk dokter ini
|
||||
doctorName := dokter.Nama_Dokter
|
||||
if doctorName == "" {
|
||||
doctorName = "Dokter"
|
||||
}
|
||||
|
||||
bodyForDokter := fmt.Sprintf(`
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||
.header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }
|
||||
.content { background-color: #f9f9f9; padding: 20px; margin-top: 20px; }
|
||||
.info-row { margin: 10px 0; }
|
||||
.label { font-weight: bold; }
|
||||
.billing-sign { font-size: 18px; font-weight: bold; padding: 10px; text-align: center; margin: 20px 0; }
|
||||
.sign-hijau { background-color: #4CAF50; color: white; }
|
||||
.sign-kuning { background-color: #FFC107; color: #333; }
|
||||
.sign-orange { background-color: #FF9800; color: white; }
|
||||
.sign-merah { background-color: #F44336; color: white; }
|
||||
.footer { margin-top: 20px; padding: 10px; text-align: center; font-size: 12px; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h2>Notifikasi Billing Sign</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Yth. Dr. %s,</p>
|
||||
<p>Berikut adalah informasi billing sign untuk pasien yang Anda tangani:</p>
|
||||
<div class="info-row"><span class="label">Nama Pasien:</span> %s</div>
|
||||
<div class="info-row"><span class="label">ID Billing:</span> %d</div>
|
||||
<div class="info-row"><span class="label">Ruangan:</span> %s</div>
|
||||
<div class="info-row"><span class="label">Kelas:</span> %s</div>
|
||||
<div class="info-row"><span class="label">Cara Bayar:</span> %s</div>
|
||||
<div class="info-row"><span class="label">Total Tarif RS:</span> Rp %.2f</div>
|
||||
<div class="info-row"><span class="label">Total Klaim BPJS:</span> Rp %.2f</div>
|
||||
<div class="billing-sign sign-%s">Billing Sign: %s</div>
|
||||
<p>Terima kasih atas perhatiannya.</p>
|
||||
</div>
|
||||
<div class="footer"><p>Sistem Billing Care IT</p><p>Email ini dikirim secara otomatis, mohon tidak membalas email ini.</p></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`, doctorName, pasien.Nama_Pasien, billing.ID_Billing, pasien.Ruangan, pasien.Kelas,
|
||||
billing.Cara_Bayar, billing.Total_Tarif_RS, billing.Total_Klaim,
|
||||
strings.ToLower(billing.Billing_sign), billingSignDisplay)
|
||||
|
||||
// kirim async ke alamat dokter ini
|
||||
go func(addrs []string, subj, body string, id int) {
|
||||
if err := SendEmailToMultiple(addrs, subj, body); err != nil {
|
||||
fmt.Printf("Warning: Gagal mengirim email ke %v untuk billing %d: %v\n", addrs, id, err)
|
||||
} else {
|
||||
fmt.Printf("Info: Email notifikasi terkirim ke %v untuk billing %d\n", addrs, id)
|
||||
}
|
||||
}(addrs, subject, bodyForDokter, billing.ID_Billing)
|
||||
}
|
||||
|
||||
if !anyEmail {
|
||||
return fmt.Errorf("tidak ada dokter dengan email yang terdaftar untuk billing ID_Billing=%d", idBilling)
|
||||
}
|
||||
|
||||
// Return immediately; actual sending berjalan di goroutine
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user