feat: require hospital code and add company APIs
This commit is contained in:
@@ -49,7 +49,6 @@ type PublicSetting struct {
|
||||
Database string `json:"database"`
|
||||
Username string `json:"username"`
|
||||
HasPassword bool `json:"has_password"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
@@ -62,7 +61,6 @@ type PoolConfig struct {
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
managementDB *sql.DB
|
||||
defaultCode string
|
||||
poolConfig PoolConfig
|
||||
credentialKey [32]byte
|
||||
settings map[string]Setting
|
||||
@@ -70,9 +68,9 @@ type Registry struct {
|
||||
retiredPools []*sql.DB
|
||||
}
|
||||
|
||||
// NewRegistry creates the management database and tables, then seeds the
|
||||
// existing HIS connection as the default hospital.
|
||||
func NewRegistry(ctx context.Context, managementSetting Setting, defaultCode string, defaultSetting Setting, poolConfig PoolConfig, credentialSecret string) (*Registry, error) {
|
||||
// NewRegistry creates the management database and tables, then loads all
|
||||
// active hospital database settings.
|
||||
func NewRegistry(ctx context.Context, managementSetting Setting, poolConfig PoolConfig, credentialSecret string) (*Registry, error) {
|
||||
if !validDatabaseName.MatchString(managementSetting.Database) {
|
||||
return nil, errors.New("nama database manajemen hanya boleh berisi huruf, angka, dan underscore")
|
||||
}
|
||||
@@ -86,7 +84,6 @@ func NewRegistry(ctx context.Context, managementSetting Setting, defaultCode str
|
||||
}
|
||||
r := &Registry{
|
||||
managementDB: managementDB,
|
||||
defaultCode: NormalizeRSCode(defaultCode),
|
||||
poolConfig: poolConfig,
|
||||
credentialKey: sha256.Sum256([]byte(credentialSecret)),
|
||||
settings: make(map[string]Setting),
|
||||
@@ -96,14 +93,6 @@ func NewRegistry(ctx context.Context, managementSetting Setting, defaultCode str
|
||||
_ = managementDB.Close()
|
||||
return nil, err
|
||||
}
|
||||
defaultSetting.RSCode = r.defaultCode
|
||||
if strings.TrimSpace(defaultSetting.Name) == "" {
|
||||
defaultSetting.Name = "RS Dev Awalbros"
|
||||
}
|
||||
if err := r.seedDefault(ctx, defaultSetting); err != nil {
|
||||
_ = managementDB.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err := r.load(ctx); err != nil {
|
||||
_ = managementDB.Close()
|
||||
return nil, err
|
||||
@@ -113,13 +102,8 @@ func NewRegistry(ctx context.Context, managementSetting Setting, defaultCode str
|
||||
|
||||
func NormalizeRSCode(code string) string { return strings.ToUpper(strings.TrimSpace(code)) }
|
||||
|
||||
func (r *Registry) DefaultCode() string { return r.defaultCode }
|
||||
|
||||
func (r *Registry) Resolve(ctx context.Context, code string) (repository.MySQLLayananRepository, error) {
|
||||
code = NormalizeRSCode(code)
|
||||
if code == "" {
|
||||
code = r.defaultCode
|
||||
}
|
||||
r.mu.RLock()
|
||||
setting, exists := r.settings[code]
|
||||
db := r.pools[code]
|
||||
@@ -174,7 +158,7 @@ func (r *Registry) Upsert(ctx context.Context, setting Setting) (PublicSetting,
|
||||
return PublicSetting{}, false, err
|
||||
}
|
||||
if created {
|
||||
result, execErr := tx.ExecContext(ctx, `INSERT INTO hospitals (code, name, is_default, active) VALUES (?, ?, 0, 1)`, setting.RSCode, setting.Name)
|
||||
result, execErr := tx.ExecContext(ctx, `INSERT INTO hospitals (code, name, active) VALUES (?, ?, 1)`, setting.RSCode, setting.Name)
|
||||
if execErr != nil {
|
||||
_ = newDB.Close()
|
||||
return PublicSetting{}, false, execErr
|
||||
@@ -211,7 +195,7 @@ func (r *Registry) Upsert(ctx context.Context, setting Setting) (PublicSetting,
|
||||
r.retiredPools = append(r.retiredPools, oldPool)
|
||||
}
|
||||
r.mu.Unlock()
|
||||
return publicSetting(setting, r.defaultCode), created, nil
|
||||
return publicSetting(setting), created, nil
|
||||
}
|
||||
|
||||
func (r *Registry) List() []PublicSetting {
|
||||
@@ -219,7 +203,7 @@ func (r *Registry) List() []PublicSetting {
|
||||
defer r.mu.RUnlock()
|
||||
result := make([]PublicSetting, 0, len(r.settings))
|
||||
for _, setting := range r.settings {
|
||||
result = append(result, publicSetting(setting, r.defaultCode))
|
||||
result = append(result, publicSetting(setting))
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool { return result[i].RSCode < result[j].RSCode })
|
||||
return result
|
||||
@@ -230,7 +214,7 @@ func (r *Registry) Get(code string) (PublicSetting, bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
setting, exists := r.settings[code]
|
||||
return publicSetting(setting, r.defaultCode), exists
|
||||
return publicSetting(setting), exists
|
||||
}
|
||||
|
||||
func (r *Registry) Close() error {
|
||||
@@ -287,7 +271,7 @@ func (r *Registry) migrate(ctx context.Context) error {
|
||||
statements := []string{
|
||||
`CREATE TABLE IF NOT EXISTS hospitals (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, code VARCHAR(50) NOT NULL, name VARCHAR(150) NOT NULL,
|
||||
is_default TINYINT(1) NOT NULL DEFAULT 0, active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id), UNIQUE KEY uq_hospitals_code (code), KEY idx_hospitals_active (active)
|
||||
@@ -307,44 +291,19 @@ func (r *Registry) migrate(ctx context.Context) error {
|
||||
return fmt.Errorf("migrasi database manajemen: %w", err)
|
||||
}
|
||||
}
|
||||
var defaultColumnExists int
|
||||
if err := r.managementDB.QueryRowContext(ctx, `SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'hospitals' AND column_name = 'is_default'`).Scan(&defaultColumnExists); err != nil {
|
||||
return fmt.Errorf("periksa schema database manajemen: %w", err)
|
||||
}
|
||||
if defaultColumnExists > 0 {
|
||||
if _, err := r.managementDB.ExecContext(ctx, `ALTER TABLE hospitals DROP COLUMN is_default`); err != nil {
|
||||
return fmt.Errorf("hapus kolom default rumah sakit: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) seedDefault(ctx context.Context, setting Setting) error {
|
||||
setting = normalizeSetting(setting)
|
||||
if err := Validate(setting); err != nil {
|
||||
return fmt.Errorf("setting database default tidak valid: %w", err)
|
||||
}
|
||||
encryptedPassword, err := r.encrypt(setting.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx, err := r.managementDB.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
if _, err := tx.ExecContext(ctx, `UPDATE hospitals SET is_default = 0 WHERE code <> ?`, setting.RSCode); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `INSERT INTO hospitals (code, name, is_default, active) VALUES (?, ?, 1, 1)
|
||||
ON DUPLICATE KEY UPDATE is_default = 1, active = 1`, setting.RSCode, setting.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hospitalID int64
|
||||
if err := tx.QueryRowContext(ctx, `SELECT id FROM hospitals WHERE code = ?`, setting.RSCode).Scan(&hospitalID); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `INSERT INTO hospital_databases (hospital_id, host, port, database_name, username, encrypted_password)
|
||||
VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE hospital_id = hospital_id`, hospitalID, setting.Host,
|
||||
setting.Port, setting.Database, setting.Username, encryptedPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *Registry) load(ctx context.Context) error {
|
||||
rows, err := r.managementDB.QueryContext(ctx, `SELECT h.code, h.name, d.host, d.port, d.database_name, d.username, d.encrypted_password
|
||||
FROM hospitals h INNER JOIN hospital_databases d ON d.hospital_id = h.id WHERE h.active = 1 ORDER BY h.code`)
|
||||
@@ -464,10 +423,10 @@ func normalizeSetting(setting Setting) Setting {
|
||||
return setting
|
||||
}
|
||||
|
||||
func publicSetting(setting Setting, defaultCode string) PublicSetting {
|
||||
func publicSetting(setting Setting) PublicSetting {
|
||||
return PublicSetting{RSCode: setting.RSCode, Name: setting.Name, Host: setting.Host, Port: setting.Port,
|
||||
Database: setting.Database, Username: setting.Username, HasPassword: setting.Password != "",
|
||||
IsDefault: setting.RSCode == defaultCode, Active: true}
|
||||
Active: true}
|
||||
}
|
||||
|
||||
func mysqlDSN(setting Setting) string {
|
||||
|
||||
Reference in New Issue
Block a user