package helpers import ( "cargo-erp-backend/internal/config" "cargo-erp-backend/internal/database" "cargo-erp-backend/internal/domain" "encoding/json" "reflect" "github.com/redis/go-redis/v9" "go.uber.org/zap" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) type HelperInterface interface { GetDB(key string) *gorm.DB GetRedis(key string) *redis.Client Config() config.ConfigInterface Log() *zap.Logger HashPassword(password string) string VerifyPassword(hashedPassword, password string) bool StringFromPtr(ptr *string) string StructTobyte(data interface{}) ([]byte, error) CloneStructWithoutField(input interface{}, field string) (map[string]interface{}, error) GetAuthInfo(authinfo string) (*domain.User, error) MapStructToStruct(source interface{}, source_tag string, target interface{}, target_tag string) } type Helper struct { Conf config.ConfigInterface DB database.DatabaseInterface Logger *zap.Logger RedisDB database.RedisInterface } func NewHelper(config config.ConfigInterface, db database.DatabaseInterface, logger *zap.Logger, dbredis database.RedisInterface) HelperInterface { return &Helper{ Conf: config, DB: db, Logger: logger, RedisDB: dbredis, } } func (h *Helper) GetDB(key string) *gorm.DB { return h.DB.Get(key) } func (h *Helper) GetRedis(key string) *redis.Client { return h.RedisDB.Get(key) } func (h *Helper) Config() config.ConfigInterface { return h.Conf } func (h *Helper) Log() *zap.Logger { return h.Logger } func (h *Helper) HashPassword(password string) string { bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(bytes) } func (h *Helper) VerifyPassword(hashedPassword, password string) bool { err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) return err == nil } func (h *Helper) StringFromPtr(ptr *string) string { if ptr == nil { return "" } return *ptr } func (h *Helper) StructTobyte(data interface{}) ([]byte, error) { var output []byte s, err := json.Marshal(data) if err != nil { return output, err } output = s return output, nil } func (h *Helper) CloneStructWithoutField(input interface{}, field string) (map[string]interface{}, error) { data, err := json.Marshal(input) if err != nil { return nil, err } var result map[string]interface{} if err := json.Unmarshal(data, &result); err != nil { return nil, err } if len(field) > 0 { delete(result, field) } return result, nil } func (h *Helper) GetAuthInfo(authinfo string) (*domain.User, error) { var auth domain.User if err := json.Unmarshal([]byte(authinfo), &auth); err != nil { return nil, err } return &auth, nil } func (h *Helper) MapStructToStruct(source interface{}, source_tag string, target interface{}, target_tag string) { srcVal := reflect.ValueOf(source) if srcVal.Kind() == reflect.Ptr { srcVal = srcVal.Elem() } srcType := srcVal.Type() // 1. Kumpulkan data dari struct asal ke dalam map [nama_tag] -> value jsonMap := make(map[string]reflect.Value) for i := 0; i < srcVal.NumField(); i++ { jsonTag := srcType.Field(i).Tag.Get(source_tag) if jsonTag != "" && jsonTag != "-" { jsonMap[jsonTag] = srcVal.Field(i) } } tgtVal := reflect.ValueOf(target) if tgtVal.Kind() != reflect.Ptr { panic("target harus berupa pointer ke struct") } tgtVal = tgtVal.Elem() tgtType := tgtVal.Type() // 2. Isi struct tujuan dengan mencocokkan tag 'column' for i := 0; i < tgtVal.NumField(); i++ { columnTag := tgtType.Field(i).Tag.Get(target_tag) if columnTag == "" { continue } // Jika ada data di map yang cocok dengan tag column, pindahkan nilainya if srcFieldVal, tersedia := jsonMap[columnTag]; tersedia { targetField := tgtVal.Field(i) // Pastikan tipe datanya sama atau bisa saling di-assign untuk menghindari panic if targetField.CanSet() && srcFieldVal.Type().AssignableTo(targetField.Type()) { targetField.Set(srcFieldVal) } } } }