Initial CPONE middleware implementation

This commit is contained in:
2026-08-18 13:41:38 +07:00
commit 676d930561
17 changed files with 1000 additions and 0 deletions

View File

@@ -0,0 +1,240 @@
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
}