package repository import ( "context" "database/sql" "errors" "fmt" "strings" ) var ErrTarifTableNotFound = errors.New("tarif table not found") type MappingHargaParams struct { PeriodeTarifID string LayananIDs []string Search string } type TarifLayanan struct { TarifLayananID int64 `json:"TarifLayananID"` LayananID *string `json:"LayananID"` NamaLayanan *string `json:"NamaLayanan"` JenisLayananID *int64 `json:"JenisLayananID"` NamaJenisLayanan *string `json:"NamaJenisLayanan"` Kelas string `json:"Kelas"` NamaKelas string `json:"NamaKelas"` Harga *string `json:"Harga"` TipeDiskonRJ string `json:"TipeDiskonRJ"` DiskonRJ string `json:"DiskonRJ"` TipeDiskonRI string `json:"TipeDiskonRI"` DiskonRI string `json:"DiskonRI"` TipeShare *string `json:"TipeShare"` ShareDokter *string `json:"ShareDokter"` ShareRS *string `json:"ShareRS"` ShareExt *string `json:"ShareExt"` ShareLain *string `json:"ShareLain"` AsalTarif string `json:"AsalTarif"` } type MySQLLayananRepository struct { db *sql.DB } func NewMySQLLayananRepository(db *sql.DB) MySQLLayananRepository { return MySQLLayananRepository{db: db} } func (r MySQLLayananRepository) MappingHarga(ctx context.Context, params MappingHargaParams) ([]TarifLayanan, error) { tableName := "harga_layanan_" + params.PeriodeTarifID exists, err := r.tableExists(ctx, tableName) if err != nil { return nil, err } if !exists { return nil, ErrTarifTableNotFound } query, args := mappingHargaQuery(tableName, params.LayananIDs, params.Search) rows, err := r.db.QueryContext(ctx, query, args...) if err != nil { return nil, err } defer rows.Close() result := make([]TarifLayanan, 0) seenLayananID := map[string]struct{}{} for rows.Next() { row, err := scanTarifLayanan(rows) if err != nil { return nil, err } layananID := stringValue(row.LayananID) if layananID != "" { if _, exists := seenLayananID[layananID]; exists { continue } seenLayananID[layananID] = struct{}{} } result = append(result, row) } if err := rows.Err(); err != nil { return nil, err } return result, nil } func (r MySQLLayananRepository) tableExists(ctx context.Context, tableName string) (bool, error) { var count int err := r.db.QueryRowContext(ctx, ` SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ? `, tableName).Scan(&count) if err != nil { return false, err } return count > 0, nil } func mappingHargaQuery(tableName string, layananIDs []string, search string) (string, []any) { args := make([]any, 0, len(layananIDs)+2) placeholders := make([]string, 0, len(layananIDs)) for _, id := range layananIDs { placeholders = append(placeholders, "?") args = append(args, id) } var builder strings.Builder builder.WriteString(` SELECT tarif.HargaLayananID AS TarifLayananID, layanan_dept.LayananID, layanan.Nama AS NamaLayanan, layanan.JenisLayananID, jenis.Nama AS NamaJenisLayanan, 'Default' AS Kelas, 'Default' AS NamaKelas, tarif.Harga, 'Percentage' AS TipeDiskonRJ, '0.00' AS DiskonRJ, 'Percentage' AS TipeDiskonRI, '0.00' AS DiskonRI, tarif.ShareTipe AS TipeShare, tarif.ShareDokter, tarif.ShareRS, tarif.ShareExternal AS ShareExt, tarif.ShareLain, '+Layanan' AS AsalTarif FROM layanandepartemen AS layanan_dept INNER JOIN `) builder.WriteString(tableName) builder.WriteString(` AS tarif ON tarif.LayananDepartemenID = layanan_dept.LayananDepartemenID AND tarif.KelasID = 'Default' AND tarif.NA = 'N' LEFT JOIN masterlayanan AS layanan ON layanan.LayananID = layanan_dept.LayananID LEFT JOIN jenislayanan AS jenis ON jenis.JenisLayananID = layanan.JenisLayananID WHERE layanan_dept.NA = 'N' AND layanan_dept.LayananID IN (` + strings.Join(placeholders, ",") + `) `) if search != "" { like := "%" + search + "%" builder.WriteString(` AND ( layanan_dept.LayananID LIKE ? OR layanan.Nama LIKE ? ) `) args = append(args, like, like) } builder.WriteString(` ORDER BY layanan.Nama, layanan.JenisLayananID `) return builder.String(), args } func scanTarifLayanan(rows *sql.Rows) (TarifLayanan, error) { var row TarifLayanan var layananID sql.NullString var namaLayanan sql.NullString var jenisLayananID sql.NullInt64 var namaJenisLayanan sql.NullString var harga sql.NullString var tipeShare sql.NullString var shareDokter sql.NullString var shareRS sql.NullString var shareExt sql.NullString var shareLain sql.NullString err := rows.Scan( &row.TarifLayananID, &layananID, &namaLayanan, &jenisLayananID, &namaJenisLayanan, &row.Kelas, &row.NamaKelas, &harga, &row.TipeDiskonRJ, &row.DiskonRJ, &row.TipeDiskonRI, &row.DiskonRI, &tipeShare, &shareDokter, &shareRS, &shareExt, &shareLain, &row.AsalTarif, ) if err != nil { return TarifLayanan{}, fmt.Errorf("scan tarif layanan: %w", err) } row.LayananID = nullStringPtr(layananID) row.NamaLayanan = nullStringPtr(namaLayanan) row.JenisLayananID = nullInt64Ptr(jenisLayananID) row.NamaJenisLayanan = nullStringPtr(namaJenisLayanan) row.Harga = nullStringPtr(harga) row.TipeShare = nullStringPtr(tipeShare) row.ShareDokter = nullStringPtr(shareDokter) row.ShareRS = nullStringPtr(shareRS) row.ShareExt = nullStringPtr(shareExt) row.ShareLain = nullStringPtr(shareLain) return row, nil } func nullStringPtr(value sql.NullString) *string { if !value.Valid { return nil } return &value.String } func nullInt64Ptr(value sql.NullInt64) *int64 { if !value.Valid { return nil } return &value.Int64 } func stringValue(value *string) string { if value == nil { return "" } return *value }