lifedebugger commited on
Commit
158bb31
·
1 Parent(s): 1177d61

Deploy files from GitHub repository

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. main.go +5 -0
  2. models/database_orm_model.go +1 -1
  3. models/exception_model.go +18 -15
  4. models/request_model.go +116 -116
  5. response/api_response_v2.go +16 -1
  6. response/validation.go +14 -0
  7. services/cv_service.go +384 -343
  8. space/go.sum +2 -0
  9. space/models/database_orm_model.go +32 -31
  10. space/models/request_model.go +62 -59
  11. space/space/controller/quiz/list_quiz_controller.go +23 -0
  12. space/space/go.mod +4 -3
  13. space/space/router/quiz_route.go +1 -0
  14. space/space/services/academy_quiz_service.go +20 -3
  15. space/space/space/config/database_connection_config.go +3 -0
  16. space/space/space/controller/cv/cv_controller.go +288 -0
  17. space/space/space/models/database_orm_model.go +39 -0
  18. space/space/space/models/request_model.go +23 -0
  19. space/space/space/repositories/cv_repository.go +117 -0
  20. space/space/space/router/cv_route.go +18 -0
  21. space/space/space/services/cv_service.go +167 -0
  22. space/space/space/space/controller/user/user_profile_controller.go +21 -21
  23. space/space/space/space/controller/user/user_update_profile_controller.go +25 -25
  24. space/space/space/space/repositories/account_repository.go +87 -87
  25. space/space/space/space/services/external_authentication_service.go +81 -81
  26. space/space/space/space/services/user_profile_service.go +4 -4
  27. space/space/space/space/space/config/database_connection_config.go +4 -0
  28. space/space/space/space/space/controller/cv/cv_controller.go +267 -0
  29. space/space/space/space/space/main.go +9 -1
  30. space/space/space/space/space/middleware/authentication_middleware.go +7 -0
  31. space/space/space/space/space/models/database_orm_model.go +118 -33
  32. space/space/space/space/space/models/request_model.go +126 -51
  33. space/space/space/space/space/repositories/cv_repository.go +126 -0
  34. space/space/space/space/space/router/cv_route.go +26 -0
  35. space/space/space/space/space/router/router.go +11 -10
  36. space/space/space/space/space/router/server.go +4 -0
  37. space/space/space/space/space/services/cv_service.go +277 -0
  38. space/space/space/space/space/space/space/.gitignore +1 -0
  39. space/space/space/space/space/space/space/controller/health_check/health_check_controller.go +12 -8
  40. space/space/space/space/space/space/space/main.go +2 -2
  41. space/space/space/space/space/space/space/models/database_orm_model.go +207 -207
  42. space/space/space/space/space/space/space/models/exception_model.go +19 -13
  43. space/space/space/space/space/space/space/models/health_check_model.go +1 -3
  44. space/space/space/space/space/space/space/repositories/health_check_repository.go +12 -13
  45. space/space/space/space/space/space/space/response/api_response_v2.go +79 -0
  46. space/space/space/space/space/space/space/response/gorm.go +62 -0
  47. space/space/space/space/space/space/space/router/server.go +2 -2
  48. space/space/space/space/space/space/space/services/health_check_service.go +31 -0
  49. space/space/space/space/space/space/space/services/user_profile_service.go +115 -115
  50. space/space/space/space/space/space/space/space/Dockerfile +6 -0
main.go CHANGED
@@ -9,6 +9,7 @@ import (
9
  "api.qobiltu.id/router"
10
  "api.qobiltu.id/services"
11
  "api.qobiltu.id/utils"
 
12
  "api.qobiltu.id/worker"
13
  "github.com/hibiken/asynq"
14
  "log/slog"
@@ -18,6 +19,10 @@ import (
18
 
19
  func main() {
20
 
 
 
 
 
21
  // setup email sender
22
  emailConfig := mail.Config{
23
  Host: config.SMTP_HOST,
 
9
  "api.qobiltu.id/router"
10
  "api.qobiltu.id/services"
11
  "api.qobiltu.id/utils"
12
+ "api.qobiltu.id/validation"
13
  "api.qobiltu.id/worker"
14
  "github.com/hibiken/asynq"
15
  "log/slog"
 
19
 
20
  func main() {
21
 
22
+ // setup validation
23
+ err := validation.New(validation.LocaleID)
24
+ utils.FatalIfErr("failed to setup validator", err)
25
+
26
  // setup email sender
27
  emailConfig := mail.Config{
28
  Host: config.SMTP_HOST,
models/database_orm_model.go CHANGED
@@ -268,7 +268,7 @@ type (
268
  ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
269
  AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
270
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
271
- LastEducation *string `gorm:"column:last_education" json:"last_education"` // pendidikan terakhir
272
  EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
273
  EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
274
  YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
 
268
  ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
269
  AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
270
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
271
+ LastEducation *string `gorm:"column:last_education" json:"last_education" validate:""` // pendidikan terakhir
272
  EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
273
  EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
274
  YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
models/exception_model.go CHANGED
@@ -1,23 +1,26 @@
1
  package models
2
 
 
 
3
  type Exception struct {
4
- Unauthorized bool `json:"unauthorized,omitempty"`
5
- BadRequest bool `json:"bad_request,omitempty"`
6
- DataNotFound bool `json:"data_not_found,omitempty"`
7
- InternalServerError bool `json:"internal_server_error,omitempty"`
8
- DataDuplicate bool `json:"data_duplicate,omitempty"`
9
- QueryError bool `json:"query_error,omitempty"`
10
- InvalidPasswordLength bool `json:"invalid_password_length,omitempty"`
11
- IsPassTheLimit bool `json:"is_pass_the_limit,omitempty"`
12
- IsTimeOut bool `json:"is_time_out,omitempty"`
13
- AttemptNotFound bool `json:"attempt_not_found,omitempty"`
14
- Forbidden bool `json:"forbidden,omitempty"`
15
- ValidationError bool `json:"validation_error,omitempty"`
16
 
17
- Message string `json:"message,omitempty"`
18
- Err error `json:"-"`
 
19
  }
20
 
21
  func (a Exception) Error() string {
22
- return a.Err.Error()
23
  }
 
1
  package models
2
 
3
+ import "api.qobiltu.id/validation"
4
+
5
  type Exception struct {
6
+ Unauthorized bool `json:"unauthorized,omitempty"`
7
+ BadRequest bool `json:"bad_request,omitempty"`
8
+ DataNotFound bool `json:"data_not_found,omitempty"`
9
+ InternalServerError bool `json:"internal_server_error,omitempty"`
10
+ DataDuplicate bool `json:"data_duplicate,omitempty"`
11
+ QueryError bool `json:"query_error,omitempty"`
12
+ InvalidPasswordLength bool `json:"invalid_password_length,omitempty"`
13
+ IsPassTheLimit bool `json:"is_pass_the_limit,omitempty"`
14
+ IsTimeOut bool `json:"is_time_out,omitempty"`
15
+ AttemptNotFound bool `json:"attempt_not_found,omitempty"`
16
+ Forbidden bool `json:"forbidden,omitempty"`
17
+ ValidationError bool `json:"validation_error,omitempty"`
18
 
19
+ Message string `json:"message,omitempty"`
20
+ Err error `json:"-"`
21
+ ValidationErrorFields []validation.ErrorMessage `json:"validation_error_fields,omitempty"`
22
  }
23
 
24
  func (a Exception) Error() string {
25
+ return a.Err.Error()
26
  }
models/request_model.go CHANGED
@@ -1,152 +1,152 @@
1
  package models
2
 
3
  import (
4
- "github.com/lib/pq"
5
- "time"
6
  )
7
 
8
  type LoginRequest struct {
9
- Email string `json:"email" binding:"required"`
10
- Password string `json:"password" binding:"required"`
11
  }
12
 
13
  type RegisterRequest struct {
14
- Name string `json:"name"`
15
- Email string `json:"email" binding:"required,email"`
16
- Phone int `json:"phone"`
17
- Password string `json:"password" binding:"required"`
18
  }
19
 
20
  type ChangePasswordRequest struct {
21
- OldPassword string `json:"old_password" binding:"required" `
22
- NewPassword string `json:"new_password" binding:"required" `
23
  }
24
 
25
  type CreateVerifyEmailRequest struct {
26
- Token uint `json:"token" binding:"required"`
27
  }
28
 
29
  type OptionsRequest struct {
30
- OptionName string `json:"option_name" binding:"required"`
31
- OptionValue []string `json:"option_values" binding:"required"`
32
  }
33
 
34
  type ExternalAuthRequest struct {
35
- OauthID string `json:"oauth_id" binding:"required"`
36
- OauthProvider string `json:"oauth_provider" binding:"required"`
37
- IsAgreeTerms bool `json:"is_agree_terms"`
38
- IsSexualDisease bool `json:"is_sexual_disease"`
39
  }
40
 
41
  type ForgotPasswordRequest struct {
42
- Email string `json:"email" binding:"required,email"`
43
  }
44
  type ValidateForgotPasswordRequest struct {
45
- Token uint `json:"token" binding:"required"`
46
- NewPassword string `json:"new_password"`
47
  }
48
 
49
  type QuestionQuizRequest struct {
50
- QuestionNo int `json:"question_no" binding:"required"`
51
  }
52
 
53
  type AnswerQuizRequest struct {
54
- QuestionNo int `json:"question_no" binding:"required"`
55
- Answer int `json:"answer" binding:"required"`
56
  }
57
 
58
  type (
59
- PersonalityAndPreferenceCVRequest struct {
60
- AccountID int64 `json:"-"`
61
- PositiveTraits *string `json:"positive_traits"` // sifat positif
62
- NegativeTraits *string `json:"negative_traits"` // sifat negatif
63
- Hobbies *string `json:"hobbies"` // hobi
64
- LifeGoals *string `json:"life_goals"` // target hidup
65
- DailyActivities *string `json:"daily_activities"` // kegiatan sehari-hari
66
- LeisureActivities *string `json:"leisure_activities"` // kegiatan waktu luang
67
- Likes *string `json:"likes"` // hal yang disukai
68
- Dislikes *string `json:"dislikes"` // hal yang tidak disukai
69
- StressHandling *string `json:"stress_handling"` // cara mengatasi stres
70
- AngerTriggers *string `json:"anger_triggers"` // pemicu amarah
71
- FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
72
- CanCook *bool `json:"can_cook"` // bisa memasak
73
- TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
74
- MonthlyExpenses *string `json:"monthly_expenses"` // pengeluaran per bulan
75
- }
76
-
77
- FamilyMemberRequest struct {
78
- AccountID int64 `json:"-"`
79
- Role *string `json:"role"` // Peran dalam keluarga
80
- Status *string `json:"status"` // Status (Hidup, Wafat)
81
- Religion *string `json:"religion"` // Agama
82
- Job *string `json:"job"` // Pekerjaan
83
- LastEducation *string `json:"last_education"` // Pendidikan terakhir
84
- Age *int `json:"age"` // Usia
85
- }
86
-
87
- PhysicalAndHealthRequest struct {
88
- AccountID int64 `json:"-"`
89
- HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
90
- WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
91
- BodyShape *string `json:"body_shape"` // Bentuk tubuh
92
- SkinColor *string `json:"skin_color"` // Warna kulit
93
- HairType *string `json:"hair_type"` // Tipe rambut
94
- MedicalHistory *string `json:"medical_history"` // Riwayat penyakit
95
- PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
96
- PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
97
- }
98
-
99
- AccountDetailsRequest struct {
100
- AccountID int64 `json:"-"`
101
- FullName *string `json:"full_name"`
102
- Gender *string `json:"gender"`
103
- DateOfBirth *time.Time `json:"date_of_birth"`
104
- PlaceOfBirth *string `json:"place_of_birth"`
105
- Domicile *string `json:"domicile"`
106
- MaritalStatus *string `json:"marital_status"`
107
- LastEducation *string `json:"last_education"`
108
- LastJob *string `json:"last_job"`
109
- }
110
-
111
- WorshipAndReligiousUnderstandingRequest struct {
112
- AccountID int64 `json:"-"`
113
- ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
114
- CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
115
- TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
116
- DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
117
- QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
118
- QuranReadingAbility *string `json:"quran_reading_ability"` // kemampuan_baca_alquran
119
- DaudFasting *string `json:"daud_fasting"` // puasa_daud
120
- AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
121
- HajjOrUmrah pq.StringArray `json:"hajj_or_umrah"` // ibadah_haji_umroh
122
- ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
123
- OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
124
- OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
125
- OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
126
- WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
127
- FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
128
- }
129
-
130
- EducationRequest struct {
131
- AccountID int64 `json:"account_id"`
132
- LastEducation *string `json:"last_education"` // pendidikan terakhir
133
- EducationInstitute *string `json:"education_institute"` // institusi pendidikan
134
- EducationMajor *string `json:"education_major"` // jurusan pendidikan
135
- YearStart *int `json:"year_start"` // tahun masuk
136
- YearGraduate *int `json:"year_graduate"` // tahun lulus
137
- }
138
-
139
- JobRequest struct {
140
- AccountID int64 `json:"account_id"`
141
- InstitutionName *string `json:"institution_name"` // nama instansi
142
- CurrentJob *string `json:"current_job"` // pekerjaan saat ini
143
- YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
144
- MonthlyIncome *string `json:"monthly_income"` // penghasilan per bulan
145
- IncomeSources pq.StringArray `json:"income_sources"` // sumber penghasilan
146
- }
147
-
148
- AchievementRequest struct {
149
- AccountID int64 `json:"account_id"`
150
- AchievementOrAward *string `json:"achievement_or_award"` // prestasi atau penghargaan
151
- }
152
  )
 
1
  package models
2
 
3
  import (
4
+ "github.com/lib/pq"
5
+ "time"
6
  )
7
 
8
  type LoginRequest struct {
9
+ Email string `json:"email" binding:"required"`
10
+ Password string `json:"password" binding:"required"`
11
  }
12
 
13
  type RegisterRequest struct {
14
+ Name string `json:"name"`
15
+ Email string `json:"email" binding:"required,email"`
16
+ Phone int `json:"phone"`
17
+ Password string `json:"password" binding:"required"`
18
  }
19
 
20
  type ChangePasswordRequest struct {
21
+ OldPassword string `json:"old_password" binding:"required" `
22
+ NewPassword string `json:"new_password" binding:"required" `
23
  }
24
 
25
  type CreateVerifyEmailRequest struct {
26
+ Token uint `json:"token" binding:"required"`
27
  }
28
 
29
  type OptionsRequest struct {
30
+ OptionName string `json:"option_name" binding:"required"`
31
+ OptionValue []string `json:"option_values" binding:"required"`
32
  }
33
 
34
  type ExternalAuthRequest struct {
35
+ OauthID string `json:"oauth_id" binding:"required"`
36
+ OauthProvider string `json:"oauth_provider" binding:"required"`
37
+ IsAgreeTerms bool `json:"is_agree_terms"`
38
+ IsSexualDisease bool `json:"is_sexual_disease"`
39
  }
40
 
41
  type ForgotPasswordRequest struct {
42
+ Email string `json:"email" binding:"required,email"`
43
  }
44
  type ValidateForgotPasswordRequest struct {
45
+ Token uint `json:"token" binding:"required"`
46
+ NewPassword string `json:"new_password"`
47
  }
48
 
49
  type QuestionQuizRequest struct {
50
+ QuestionNo int `json:"question_no" binding:"required"`
51
  }
52
 
53
  type AnswerQuizRequest struct {
54
+ QuestionNo int `json:"question_no" binding:"required"`
55
+ Answer int `json:"answer" binding:"required"`
56
  }
57
 
58
  type (
59
+ PersonalityAndPreferenceCVRequest struct {
60
+ AccountID int64 `json:"-"`
61
+ PositiveTraits *string `json:"positive_traits"` // sifat positif
62
+ NegativeTraits *string `json:"negative_traits"` // sifat negatif
63
+ Hobbies *string `json:"hobbies"` // hobi
64
+ LifeGoals *string `json:"life_goals"` // target hidup
65
+ DailyActivities *string `json:"daily_activities"` // kegiatan sehari-hari
66
+ LeisureActivities *string `json:"leisure_activities"` // kegiatan waktu luang
67
+ Likes *string `json:"likes"` // hal yang disukai
68
+ Dislikes *string `json:"dislikes"` // hal yang tidak disukai
69
+ StressHandling *string `json:"stress_handling"` // cara mengatasi stres
70
+ AngerTriggers *string `json:"anger_triggers"` // pemicu amarah
71
+ FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
72
+ CanCook *bool `json:"can_cook"` // bisa memasak
73
+ TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
74
+ MonthlyExpenses *string `json:"monthly_expenses" validate:"monthly_expenses"` // pengeluaran per bulan
75
+ }
76
+
77
+ FamilyMemberRequest struct {
78
+ AccountID int64 `json:"-"`
79
+ Role *string `json:"role" validate:"family_role"` // Peran dalam keluarga
80
+ Status *string `json:"status" validate:"life_status"` // Status (Hidup, Wafat)
81
+ Religion *string `json:"religion" validate:"religion"` // Agama
82
+ Job *string `json:"job"` // Pekerjaan
83
+ LastEducation *string `json:"last_education" validate:"last_education"` // Pendidikan terakhir
84
+ Age *int `json:"age"` // Usia
85
+ }
86
+
87
+ PhysicalAndHealthRequest struct {
88
+ AccountID int64 `json:"-"`
89
+ HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
90
+ WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
91
+ BodyShape *string `json:"body_shape" validate:"body_shape"` // Bentuk tubuh
92
+ SkinColor *string `json:"skin_color" validate:"skin_color"` // Warna kulit
93
+ HairType *string `json:"hair_type" validate:"hair_type"` // Tipe rambut
94
+ MedicalHistory *string `json:"medical_history"` // Riwayat penyakit
95
+ PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
96
+ PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
97
+ }
98
+
99
+ AccountDetailsRequest struct {
100
+ AccountID int64 `json:"-"`
101
+ FullName *string `json:"full_name"`
102
+ Gender *string `json:"gender" validate:"gender"`
103
+ DateOfBirth *time.Time `json:"date_of_birth"`
104
+ PlaceOfBirth *string `json:"place_of_birth"`
105
+ Domicile *string `json:"domicile"`
106
+ MaritalStatus *string `json:"marital_status" validate:"marital_status"`
107
+ LastEducation *string `json:"last_education" validate:"last_education"`
108
+ LastJob *string `json:"last_job"`
109
+ }
110
+
111
+ WorshipAndReligiousUnderstandingRequest struct {
112
+ AccountID int64 `json:"-"`
113
+ ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
114
+ CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
115
+ TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
116
+ DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
117
+ QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
118
+ QuranReadingAbility *string `json:"quran_reading_ability" validate:"quran_reading_ability"` // kemampuan_baca_alquran
119
+ DaudFasting *string `json:"daud_fasting"` // puasa_daud
120
+ AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
121
+ HajjOrUmrah pq.StringArray `json:"hajj_or_umrah"` // ibadah_haji_umroh
122
+ ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
123
+ OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
124
+ OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
125
+ OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
126
+ WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
127
+ FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
128
+ }
129
+
130
+ EducationRequest struct {
131
+ AccountID int64 `json:"account_id"`
132
+ LastEducation *string `json:"last_education" validate:"last_education"` // pendidikan terakhir
133
+ EducationInstitute *string `json:"education_institute"` // institusi pendidikan
134
+ EducationMajor *string `json:"education_major"` // jurusan pendidikan
135
+ YearStart *int `json:"year_start"` // tahun masuk
136
+ YearGraduate *int `json:"year_graduate"` // tahun lulus
137
+ }
138
+
139
+ JobRequest struct {
140
+ AccountID int64 `json:"account_id"`
141
+ InstitutionName *string `json:"institution_name"` // nama instansi
142
+ CurrentJob *string `json:"current_job"` // pekerjaan saat ini
143
+ YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
144
+ MonthlyIncome *string `json:"monthly_income" validate:"monthly_income"` // penghasilan per bulan
145
+ IncomeSources pq.StringArray `json:"income_sources"` // sumber penghasilan
146
+ }
147
+
148
+ AchievementRequest struct {
149
+ AccountID int64 `json:"account_id"`
150
+ AchievementOrAward *string `json:"achievement_or_award"` // prestasi atau penghargaan
151
+ }
152
  )
response/api_response_v2.go CHANGED
@@ -3,6 +3,7 @@ package response
3
  import (
4
  "api.qobiltu.id/models"
5
  "api.qobiltu.id/utils"
 
6
  "errors"
7
  "net/http"
8
 
@@ -39,7 +40,7 @@ func HandleError(c *gin.Context, err error) {
39
  case exception.AttemptNotFound:
40
  responseError(c, http.StatusNotFound, exception)
41
  case exception.ValidationError:
42
- responseError(c, http.StatusUnprocessableEntity, exception)
43
  default:
44
  responseError(c, http.StatusInternalServerError, exception)
45
  }
@@ -77,3 +78,17 @@ func responseError(c *gin.Context, status int, exception models.Exception) {
77
  c.AbortWithStatusJSON(status, res)
78
  return
79
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import (
4
  "api.qobiltu.id/models"
5
  "api.qobiltu.id/utils"
6
+ "api.qobiltu.id/validation"
7
  "errors"
8
  "net/http"
9
 
 
40
  case exception.AttemptNotFound:
41
  responseError(c, http.StatusNotFound, exception)
42
  case exception.ValidationError:
43
+ responseValidationError(c, http.StatusUnprocessableEntity, exception.ValidationErrorFields) // Gunakan fungsi khusus untuk validasi
44
  default:
45
  responseError(c, http.StatusInternalServerError, exception)
46
  }
 
78
  c.AbortWithStatusJSON(status, res)
79
  return
80
  }
81
+
82
+ func responseValidationError(c *gin.Context, status int, validationErrors []validation.ErrorMessage) {
83
+ res := models.ErrorResponse{
84
+ Status: "error",
85
+ Message: "Validasi data gagal.",
86
+ Errors: models.Exception{
87
+ ValidationError: true,
88
+ ValidationErrorFields: validationErrors,
89
+ },
90
+ }
91
+
92
+ c.AbortWithStatusJSON(status, res)
93
+ return
94
+ }
response/validation.go ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package response
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ "api.qobiltu.id/validation"
6
+ )
7
+
8
+ func HandleValidationError(validationErrors []validation.ErrorMessage) error {
9
+ return models.Exception{
10
+ ValidationError: true,
11
+ Message: "Validation failed",
12
+ ValidationErrorFields: validationErrors,
13
+ }
14
+ }
services/cv_service.go CHANGED
@@ -1,444 +1,485 @@
1
  package services
2
 
3
  import (
4
- "api.qobiltu.id/models"
5
- "api.qobiltu.id/repositories"
6
- "api.qobiltu.id/response"
7
- "context"
8
- "errors"
9
- "gorm.io/gorm"
 
10
  )
11
 
12
  type CVService interface {
13
- SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error)
14
- GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error)
15
-
16
- SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error)
17
- GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error)
18
-
19
- CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
20
- UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
21
- ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error)
22
- GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error)
23
- DeleteFamilyMember(ctx context.Context, id int64) error
24
-
25
- SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error)
26
- GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error)
27
-
28
- SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
29
- GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error)
30
-
31
- CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error)
32
- UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error)
33
- ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error)
34
- GetEducation(ctx context.Context, id int64) (*models.EducationCV, error)
35
- DeleteEducation(ctx context.Context, id int64) error
36
-
37
- CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error)
38
- UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error)
39
- ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error)
40
- GetJob(ctx context.Context, id int64) (*models.JobCV, error)
41
- DeleteJob(ctx context.Context, id int64) error
42
-
43
- CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error)
44
- UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error)
45
- ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error)
46
- GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error)
47
- DeleteAchievement(ctx context.Context, id int64) error
48
  }
49
 
50
  type cvService struct {
51
- cvRepository repositories.CVRepository
52
  }
53
 
54
  func NewCVService(cvRepository repositories.CVRepository) CVService {
55
- return &cvService{
56
- cvRepository: cvRepository,
57
- }
58
  }
59
 
60
  func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error) {
61
- // Ambil data lama jika ada
62
- accountDetails, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, req.AccountID)
63
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
64
- return nil, response.HandleGormError(err, "Internal Server Error")
65
- }
66
-
67
- // Apply perubahan
68
- if accountDetails == nil {
69
- accountDetails = &models.AccountDetails{}
70
- }
71
-
72
- accountDetails.AccountID = uint(req.AccountID)
73
- accountDetails.FullName = req.FullName
74
- accountDetails.Gender = req.Gender
75
- accountDetails.DateOfBirth = req.DateOfBirth
76
- accountDetails.PlaceOfBirth = req.PlaceOfBirth
77
- accountDetails.Domicile = req.Domicile
78
- accountDetails.MaritalStatus = req.MaritalStatus
79
- accountDetails.LastEducation = req.LastEducation
80
- accountDetails.LastJob = req.LastJob
81
-
82
- // Simpan data
83
- res, err := s.cvRepository.SaveAccountDetails(ctx, accountDetails)
84
- if err != nil {
85
- return nil, response.HandleGormError(err, "Internal Server Error")
86
- }
87
-
88
- return res, nil
 
 
 
 
89
  }
90
 
91
  func (s *cvService) GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error) {
92
- res, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, id)
93
- if err != nil {
94
- return nil, response.HandleGormError(err, "Data diri tidak ditemukan")
95
- }
96
- return res, nil
97
  }
98
 
99
  func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error) {
100
- // Ambil data lama jika ada
101
- personalityAndPreference, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, req.AccountID)
102
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
103
- return nil, response.HandleGormError(err, "Internal Server Error")
104
- }
105
-
106
- // Apply perubahan
107
- if personalityAndPreference == nil {
108
- personalityAndPreference = &models.PersonalityAndPreferenceCV{}
109
- }
110
-
111
- personalityAndPreference.AccountID = req.AccountID
112
- personalityAndPreference.PositiveTraits = req.PositiveTraits
113
- personalityAndPreference.NegativeTraits = req.NegativeTraits
114
- personalityAndPreference.Hobbies = req.Hobbies
115
- personalityAndPreference.LifeGoals = req.LifeGoals
116
- personalityAndPreference.DailyActivities = req.DailyActivities
117
- personalityAndPreference.LeisureActivities = req.LeisureActivities
118
- personalityAndPreference.Likes = req.Likes
119
- personalityAndPreference.Dislikes = req.Dislikes
120
- personalityAndPreference.StressHandling = req.StressHandling
121
- personalityAndPreference.AngerTriggers = req.AngerTriggers
122
- personalityAndPreference.FavoriteFoodAndDrinks = req.FavoriteFoodAndDrinks
123
- personalityAndPreference.CanCook = req.CanCook
124
- personalityAndPreference.TypesOfDishesCooked = req.TypesOfDishesCooked
125
- personalityAndPreference.MonthlyExpenses = req.MonthlyExpenses
126
-
127
- res, err := s.cvRepository.SavePersonalityAndPreference(ctx, personalityAndPreference)
128
- if err != nil {
129
- return nil, response.HandleGormError(err, "Internal Server Error")
130
- }
131
-
132
- return res, nil
 
 
 
 
133
  }
134
 
135
  func (s *cvService) GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error) {
136
- res, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, id)
137
- if err != nil {
138
- return nil, response.HandleGormError(err, "Internal Server Error")
139
- }
140
 
141
- return res, nil
142
  }
143
 
144
  func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
145
- // Mapping request ke model
146
- familyMember := &models.FamilyMemberCV{
147
- AccountID: req.AccountID,
148
- Role: req.Role,
149
- Status: req.Status,
150
- Religion: req.Religion,
151
- Job: req.Job,
152
- LastEducation: req.LastEducation,
153
- Age: req.Age,
154
- }
155
-
156
- // Simpan ke repository
157
- res, err := s.cvRepository.SaveFamilyMember(ctx, familyMember)
158
- if err != nil {
159
- return nil, response.HandleGormError(err, "Gagal menyimpan anggota keluarga")
160
- }
161
-
162
- return res, nil
 
 
 
 
163
  }
164
 
165
  func (s *cvService) ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error) {
166
- list, err := s.cvRepository.ListFamilyMember(ctx, accountID)
167
- if err != nil {
168
- return nil, response.HandleGormError(err, "Gagal mengambil daftar anggota keluarga")
169
- }
170
- return list, nil
171
  }
172
 
173
  func (s *cvService) GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error) {
174
- res, err := s.cvRepository.GetFamilyMember(ctx, id)
175
- if err != nil {
176
- return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
177
- }
178
- return res, nil
179
  }
180
 
181
  func (s *cvService) DeleteFamilyMember(ctx context.Context, id int64) error {
182
- err := s.cvRepository.DeleteFamilyMember(ctx, id)
183
- if err != nil {
184
- return response.HandleGormError(err, "Gagal menghapus anggota keluarga")
185
- }
186
- return nil
187
  }
188
 
189
  func (s *cvService) UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
190
- existing, err := s.cvRepository.GetFamilyMember(ctx, id)
191
- if err != nil {
192
- return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
193
- }
194
-
195
- existing.Role = req.Role
196
- existing.Status = req.Status
197
- existing.Religion = req.Religion
198
- existing.Job = req.Job
199
- existing.LastEducation = req.LastEducation
200
- existing.Age = req.Age
201
-
202
- updated, err := s.cvRepository.SaveFamilyMember(ctx, existing)
203
- if err != nil {
204
- return nil, response.HandleGormError(err, "Gagal memperbarui anggota keluarga")
205
- }
206
-
207
- return updated, nil
 
 
 
 
208
  }
209
 
210
  func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
211
- // Cek apakah data sudah ada berdasarkan account_id
212
- existing, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, req.AccountID)
213
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
214
- return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data fisik dan kesehatan")
215
- }
216
-
217
- // Jika belum ada, buat objek baru
218
- if existing == nil {
219
- existing = &models.PhysicalAndHealthCV{}
220
- }
221
-
222
- // Mapping field dari request
223
- existing.AccountID = req.AccountID
224
- existing.HeightInCm = req.HeightInCm
225
- existing.WeightInKg = req.WeightInKg
226
- existing.BodyShape = req.BodyShape
227
- existing.SkinColor = req.SkinColor
228
- existing.HairType = req.HairType
229
- existing.MedicalHistory = req.MedicalHistory
230
- existing.PhysicalDisorder = req.PhysicalDisorder
231
- existing.PhysicalTraits = req.PhysicalTraits
232
-
233
- // Simpan data
234
- res, err := s.cvRepository.SavePhysicalAndHealth(ctx, existing)
235
- if err != nil {
236
- return nil, response.HandleGormError(err, "Gagal menyimpan data fisik dan kesehatan")
237
- }
238
-
239
- return res, nil
 
 
 
 
240
  }
241
 
242
  func (s *cvService) GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error) {
243
- res, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, id)
244
- if err != nil {
245
- return nil, response.HandleGormError(err, "Data fisik dan kesehatan tidak ditemukan")
246
- }
247
- return res, nil
248
  }
249
 
250
  func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
251
- // Cek apakah data sudah ada berdasarkan account_id
252
- worshipAndReligiousUnderstanding, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, req.AccountID)
253
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
254
- return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data agama dan pemahaman agama")
255
- }
256
-
257
- // Jika belum ada, buat objek baru
258
- if worshipAndReligiousUnderstanding == nil {
259
- worshipAndReligiousUnderstanding = &models.WorshipAndReligiousUnderstandingCV{}
260
- }
261
-
262
- // Mapping field dari request
263
- worshipAndReligiousUnderstanding.AccountID = req.AccountID
264
- worshipAndReligiousUnderstanding.ObligatoryPrayer = req.ObligatoryPrayer
265
- worshipAndReligiousUnderstanding.CongregationalPrayer = req.CongregationalPrayer
266
- worshipAndReligiousUnderstanding.TahajjudPrayer = req.TahajjudPrayer
267
- worshipAndReligiousUnderstanding.DhuhaPrayer = req.DhuhaPrayer
268
- worshipAndReligiousUnderstanding.QuranMemorization = req.QuranMemorization
269
- worshipAndReligiousUnderstanding.QuranReadingAbility = req.QuranReadingAbility
270
- worshipAndReligiousUnderstanding.DaudFasting = req.DaudFasting
271
- worshipAndReligiousUnderstanding.AyyamulBidhFasting = req.AyyamulBidhFasting
272
- worshipAndReligiousUnderstanding.HajjOrUmrah = req.HajjOrUmrah
273
- worshipAndReligiousUnderstanding.ListeningToMusic = req.ListeningToMusic
274
- worshipAndReligiousUnderstanding.OpinionOnIkhtilat = req.OpinionOnIkhtilat
275
- worshipAndReligiousUnderstanding.OpinionOnTouchingNonMahram = req.OpinionOnTouchingNonMahram
276
- worshipAndReligiousUnderstanding.OpinionOnVeil = req.OpinionOnVeil
277
- worshipAndReligiousUnderstanding.WeeklyReligiousStudies = req.WeeklyReligiousStudies
278
- worshipAndReligiousUnderstanding.FollowedUstadz = req.FollowedUstadz
279
-
280
- // Simpan data
281
- res, err := s.cvRepository.SaveWorshipAndReligiousUnderstanding(ctx, worshipAndReligiousUnderstanding)
282
- if err != nil {
283
- return nil, response.HandleGormError(err, "Internal Server Error")
284
- }
285
-
286
- return res, nil
 
 
 
 
287
  }
288
 
289
  func (s *cvService) GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error) {
290
- res, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, id)
291
- if err != nil {
292
- return nil, response.HandleGormError(err, "Data agama dan pemahaman agama tidak ditemukan")
293
- }
294
- return res, nil
295
  }
296
 
297
  func (s *cvService) CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error) {
298
- edu := &models.EducationCV{
299
- AccountID: req.AccountID,
300
- LastEducation: req.LastEducation,
301
- EducationInstitute: req.EducationInstitute,
302
- EducationMajor: req.EducationMajor,
303
- YearStart: req.YearStart,
304
- YearGraduate: req.YearGraduate,
305
- }
306
-
307
- res, err := s.cvRepository.SaveEducation(ctx, edu)
308
- if err != nil {
309
- return nil, response.HandleGormError(err, "Gagal menambahkan data pendidikan")
310
- }
311
-
312
- return res, nil
 
 
 
 
313
  }
314
 
315
  func (s *cvService) UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error) {
316
- edu, err := s.cvRepository.GetEducation(ctx, id)
317
- if err != nil {
318
- return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
319
- }
320
-
321
- edu.LastEducation = req.LastEducation
322
- edu.EducationInstitute = req.EducationInstitute
323
- edu.EducationMajor = req.EducationMajor
324
- edu.YearStart = req.YearStart
325
- edu.YearGraduate = req.YearGraduate
326
-
327
- res, err := s.cvRepository.SaveEducation(ctx, edu)
328
- if err != nil {
329
- return nil, response.HandleGormError(err, "Gagal memperbarui data pendidikan")
330
- }
331
- return res, nil
 
 
 
 
332
  }
333
 
334
  func (s *cvService) ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error) {
335
- return s.cvRepository.ListEducation(ctx, accountID)
336
  }
337
 
338
  func (s *cvService) GetEducation(ctx context.Context, id int64) (*models.EducationCV, error) {
339
- edu, err := s.cvRepository.GetEducation(ctx, id)
340
- if err != nil {
341
- return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
342
- }
343
- return edu, nil
344
  }
345
 
346
  func (s *cvService) DeleteEducation(ctx context.Context, id int64) error {
347
- return s.cvRepository.DeleteEducation(ctx, id)
348
  }
349
 
350
  func (s *cvService) CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error) {
351
- job := &models.JobCV{
352
- AccountID: req.AccountID,
353
- InstitutionName: req.InstitutionName,
354
- CurrentJob: req.CurrentJob,
355
- YearStartedWorking: req.YearStartedWorking,
356
- MonthlyIncome: req.MonthlyIncome,
357
- IncomeSources: req.IncomeSources,
358
- }
359
- res, err := s.cvRepository.SaveJob(ctx, job)
360
- if err != nil {
361
- return nil, response.HandleGormError(err, "Gagal menambahkan data pekerjaan")
362
- }
363
- return res, nil
 
 
 
 
364
  }
365
 
366
  func (s *cvService) UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error) {
367
- job, err := s.cvRepository.GetJob(ctx, id)
368
- if err != nil {
369
- return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
370
- }
371
-
372
- job.InstitutionName = req.InstitutionName
373
- job.CurrentJob = req.CurrentJob
374
- job.YearStartedWorking = req.YearStartedWorking
375
- job.MonthlyIncome = req.MonthlyIncome
376
- job.IncomeSources = req.IncomeSources
377
-
378
- res, err := s.cvRepository.SaveJob(ctx, job)
379
- if err != nil {
380
- return nil, response.HandleGormError(err, "Gagal memperbarui data pekerjaan")
381
- }
382
- return res, nil
 
 
 
 
383
  }
384
 
385
  func (s *cvService) ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error) {
386
- return s.cvRepository.ListJob(ctx, accountID)
387
  }
388
 
389
  func (s *cvService) GetJob(ctx context.Context, id int64) (*models.JobCV, error) {
390
- job, err := s.cvRepository.GetJob(ctx, id)
391
- if err != nil {
392
- return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
393
- }
394
- return job, nil
395
  }
396
 
397
  func (s *cvService) DeleteJob(ctx context.Context, id int64) error {
398
- return s.cvRepository.DeleteJob(ctx, id)
399
  }
400
 
401
  func (s *cvService) CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error) {
402
- ach := &models.AchievementCV{
403
- AccountID: req.AccountID,
404
- AchievementOrAward: req.AchievementOrAward,
405
- }
406
- res, err := s.cvRepository.SaveAchievement(ctx, ach)
407
- if err != nil {
408
- return nil, response.HandleGormError(err, "Gagal menambahkan data prestasi")
409
- }
410
-
411
- return res, nil
412
  }
413
 
414
  func (s *cvService) UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error) {
415
- ach, err := s.cvRepository.GetAchievement(ctx, id)
416
- if err != nil {
417
- return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
418
- }
419
 
420
- ach.AchievementOrAward = req.AchievementOrAward
421
 
422
- res, err := s.cvRepository.SaveAchievement(ctx, ach)
423
- if err != nil {
424
- return nil, response.HandleGormError(err, "Gagal memperbarui data prestasi")
425
- }
426
 
427
- return res, nil
428
  }
429
 
430
  func (s *cvService) ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error) {
431
- return s.cvRepository.ListAchievement(ctx, accountID)
432
  }
433
 
434
  func (s *cvService) GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error) {
435
- ach, err := s.cvRepository.GetAchievement(ctx, id)
436
- if err != nil {
437
- return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
438
- }
439
- return ach, nil
440
  }
441
 
442
  func (s *cvService) DeleteAchievement(ctx context.Context, id int64) error {
443
- return s.cvRepository.DeleteAchievement(ctx, id)
444
  }
 
1
  package services
2
 
3
  import (
4
+ "api.qobiltu.id/models"
5
+ "api.qobiltu.id/repositories"
6
+ "api.qobiltu.id/response"
7
+ "api.qobiltu.id/validation"
8
+ "context"
9
+ "errors"
10
+ "gorm.io/gorm"
11
  )
12
 
13
  type CVService interface {
14
+ SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error)
15
+ GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error)
16
+
17
+ SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error)
18
+ GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error)
19
+
20
+ CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
21
+ UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
22
+ ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error)
23
+ GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error)
24
+ DeleteFamilyMember(ctx context.Context, id int64) error
25
+
26
+ SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error)
27
+ GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error)
28
+
29
+ SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
30
+ GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error)
31
+
32
+ CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error)
33
+ UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error)
34
+ ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error)
35
+ GetEducation(ctx context.Context, id int64) (*models.EducationCV, error)
36
+ DeleteEducation(ctx context.Context, id int64) error
37
+
38
+ CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error)
39
+ UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error)
40
+ ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error)
41
+ GetJob(ctx context.Context, id int64) (*models.JobCV, error)
42
+ DeleteJob(ctx context.Context, id int64) error
43
+
44
+ CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error)
45
+ UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error)
46
+ ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error)
47
+ GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error)
48
+ DeleteAchievement(ctx context.Context, id int64) error
49
  }
50
 
51
  type cvService struct {
52
+ cvRepository repositories.CVRepository
53
  }
54
 
55
  func NewCVService(cvRepository repositories.CVRepository) CVService {
56
+ return &cvService{
57
+ cvRepository: cvRepository,
58
+ }
59
  }
60
 
61
  func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error) {
62
+ if err := validation.Validate(req); err != nil {
63
+ return nil, response.HandleValidationError(err)
64
+ }
65
+
66
+ // Ambil data lama jika ada
67
+ accountDetails, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, req.AccountID)
68
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
69
+ return nil, response.HandleGormError(err, "Internal Server Error")
70
+ }
71
+
72
+ // Apply perubahan
73
+ if accountDetails == nil {
74
+ accountDetails = &models.AccountDetails{}
75
+ }
76
+
77
+ accountDetails.AccountID = uint(req.AccountID)
78
+ accountDetails.FullName = req.FullName
79
+ accountDetails.Gender = req.Gender
80
+ accountDetails.DateOfBirth = req.DateOfBirth
81
+ accountDetails.PlaceOfBirth = req.PlaceOfBirth
82
+ accountDetails.Domicile = req.Domicile
83
+ accountDetails.MaritalStatus = req.MaritalStatus
84
+ accountDetails.LastEducation = req.LastEducation
85
+ accountDetails.LastJob = req.LastJob
86
+
87
+ // Simpan data
88
+ res, err := s.cvRepository.SaveAccountDetails(ctx, accountDetails)
89
+ if err != nil {
90
+ return nil, response.HandleGormError(err, "Internal Server Error")
91
+ }
92
+
93
+ return res, nil
94
  }
95
 
96
  func (s *cvService) GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error) {
97
+ res, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, id)
98
+ if err != nil {
99
+ return nil, response.HandleGormError(err, "Data diri tidak ditemukan")
100
+ }
101
+ return res, nil
102
  }
103
 
104
  func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error) {
105
+ if err := validation.Validate(req); err != nil {
106
+ return nil, response.HandleValidationError(err)
107
+ }
108
+
109
+ // Ambil data lama jika ada
110
+ personalityAndPreference, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, req.AccountID)
111
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
112
+ return nil, response.HandleGormError(err, "Internal Server Error")
113
+ }
114
+
115
+ // Apply perubahan
116
+ if personalityAndPreference == nil {
117
+ personalityAndPreference = &models.PersonalityAndPreferenceCV{}
118
+ }
119
+
120
+ personalityAndPreference.AccountID = req.AccountID
121
+ personalityAndPreference.PositiveTraits = req.PositiveTraits
122
+ personalityAndPreference.NegativeTraits = req.NegativeTraits
123
+ personalityAndPreference.Hobbies = req.Hobbies
124
+ personalityAndPreference.LifeGoals = req.LifeGoals
125
+ personalityAndPreference.DailyActivities = req.DailyActivities
126
+ personalityAndPreference.LeisureActivities = req.LeisureActivities
127
+ personalityAndPreference.Likes = req.Likes
128
+ personalityAndPreference.Dislikes = req.Dislikes
129
+ personalityAndPreference.StressHandling = req.StressHandling
130
+ personalityAndPreference.AngerTriggers = req.AngerTriggers
131
+ personalityAndPreference.FavoriteFoodAndDrinks = req.FavoriteFoodAndDrinks
132
+ personalityAndPreference.CanCook = req.CanCook
133
+ personalityAndPreference.TypesOfDishesCooked = req.TypesOfDishesCooked
134
+ personalityAndPreference.MonthlyExpenses = req.MonthlyExpenses
135
+
136
+ res, err := s.cvRepository.SavePersonalityAndPreference(ctx, personalityAndPreference)
137
+ if err != nil {
138
+ return nil, response.HandleGormError(err, "Internal Server Error")
139
+ }
140
+
141
+ return res, nil
142
  }
143
 
144
  func (s *cvService) GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error) {
145
+ res, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, id)
146
+ if err != nil {
147
+ return nil, response.HandleGormError(err, "Internal Server Error")
148
+ }
149
 
150
+ return res, nil
151
  }
152
 
153
  func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
154
+ if err := validation.Validate(req); err != nil {
155
+ return nil, response.HandleValidationError(err)
156
+ }
157
+
158
+ // Mapping request ke model
159
+ familyMember := &models.FamilyMemberCV{
160
+ AccountID: req.AccountID,
161
+ Role: req.Role,
162
+ Status: req.Status,
163
+ Religion: req.Religion,
164
+ Job: req.Job,
165
+ LastEducation: req.LastEducation,
166
+ Age: req.Age,
167
+ }
168
+
169
+ // Simpan ke repository
170
+ res, err := s.cvRepository.SaveFamilyMember(ctx, familyMember)
171
+ if err != nil {
172
+ return nil, response.HandleGormError(err, "Gagal menyimpan anggota keluarga")
173
+ }
174
+
175
+ return res, nil
176
  }
177
 
178
  func (s *cvService) ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error) {
179
+ list, err := s.cvRepository.ListFamilyMember(ctx, accountID)
180
+ if err != nil {
181
+ return nil, response.HandleGormError(err, "Gagal mengambil daftar anggota keluarga")
182
+ }
183
+ return list, nil
184
  }
185
 
186
  func (s *cvService) GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error) {
187
+ res, err := s.cvRepository.GetFamilyMember(ctx, id)
188
+ if err != nil {
189
+ return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
190
+ }
191
+ return res, nil
192
  }
193
 
194
  func (s *cvService) DeleteFamilyMember(ctx context.Context, id int64) error {
195
+ err := s.cvRepository.DeleteFamilyMember(ctx, id)
196
+ if err != nil {
197
+ return response.HandleGormError(err, "Gagal menghapus anggota keluarga")
198
+ }
199
+ return nil
200
  }
201
 
202
  func (s *cvService) UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
203
+ if err := validation.Validate(req); err != nil {
204
+ return nil, response.HandleValidationError(err)
205
+ }
206
+
207
+ existing, err := s.cvRepository.GetFamilyMember(ctx, id)
208
+ if err != nil {
209
+ return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
210
+ }
211
+
212
+ existing.Role = req.Role
213
+ existing.Status = req.Status
214
+ existing.Religion = req.Religion
215
+ existing.Job = req.Job
216
+ existing.LastEducation = req.LastEducation
217
+ existing.Age = req.Age
218
+
219
+ updated, err := s.cvRepository.SaveFamilyMember(ctx, existing)
220
+ if err != nil {
221
+ return nil, response.HandleGormError(err, "Gagal memperbarui anggota keluarga")
222
+ }
223
+
224
+ return updated, nil
225
  }
226
 
227
  func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
228
+ if err := validation.Validate(req); err != nil {
229
+ return nil, response.HandleValidationError(err)
230
+ }
231
+
232
+ // Cek apakah data sudah ada berdasarkan account_id
233
+ existing, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, req.AccountID)
234
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
235
+ return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data fisik dan kesehatan")
236
+ }
237
+
238
+ // Jika belum ada, buat objek baru
239
+ if existing == nil {
240
+ existing = &models.PhysicalAndHealthCV{}
241
+ }
242
+
243
+ // Mapping field dari request
244
+ existing.AccountID = req.AccountID
245
+ existing.HeightInCm = req.HeightInCm
246
+ existing.WeightInKg = req.WeightInKg
247
+ existing.BodyShape = req.BodyShape
248
+ existing.SkinColor = req.SkinColor
249
+ existing.HairType = req.HairType
250
+ existing.MedicalHistory = req.MedicalHistory
251
+ existing.PhysicalDisorder = req.PhysicalDisorder
252
+ existing.PhysicalTraits = req.PhysicalTraits
253
+
254
+ // Simpan data
255
+ res, err := s.cvRepository.SavePhysicalAndHealth(ctx, existing)
256
+ if err != nil {
257
+ return nil, response.HandleGormError(err, "Gagal menyimpan data fisik dan kesehatan")
258
+ }
259
+
260
+ return res, nil
261
  }
262
 
263
  func (s *cvService) GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error) {
264
+ res, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, id)
265
+ if err != nil {
266
+ return nil, response.HandleGormError(err, "Data fisik dan kesehatan tidak ditemukan")
267
+ }
268
+ return res, nil
269
  }
270
 
271
  func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
272
+ if err := validation.Validate(req); err != nil {
273
+ return nil, response.HandleValidationError(err)
274
+ }
275
+
276
+ // Cek apakah data sudah ada berdasarkan account_id
277
+ worshipAndReligiousUnderstanding, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, req.AccountID)
278
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
279
+ return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data agama dan pemahaman agama")
280
+ }
281
+
282
+ // Jika belum ada, buat objek baru
283
+ if worshipAndReligiousUnderstanding == nil {
284
+ worshipAndReligiousUnderstanding = &models.WorshipAndReligiousUnderstandingCV{}
285
+ }
286
+
287
+ // Mapping field dari request
288
+ worshipAndReligiousUnderstanding.AccountID = req.AccountID
289
+ worshipAndReligiousUnderstanding.ObligatoryPrayer = req.ObligatoryPrayer
290
+ worshipAndReligiousUnderstanding.CongregationalPrayer = req.CongregationalPrayer
291
+ worshipAndReligiousUnderstanding.TahajjudPrayer = req.TahajjudPrayer
292
+ worshipAndReligiousUnderstanding.DhuhaPrayer = req.DhuhaPrayer
293
+ worshipAndReligiousUnderstanding.QuranMemorization = req.QuranMemorization
294
+ worshipAndReligiousUnderstanding.QuranReadingAbility = req.QuranReadingAbility
295
+ worshipAndReligiousUnderstanding.DaudFasting = req.DaudFasting
296
+ worshipAndReligiousUnderstanding.AyyamulBidhFasting = req.AyyamulBidhFasting
297
+ worshipAndReligiousUnderstanding.HajjOrUmrah = req.HajjOrUmrah
298
+ worshipAndReligiousUnderstanding.ListeningToMusic = req.ListeningToMusic
299
+ worshipAndReligiousUnderstanding.OpinionOnIkhtilat = req.OpinionOnIkhtilat
300
+ worshipAndReligiousUnderstanding.OpinionOnTouchingNonMahram = req.OpinionOnTouchingNonMahram
301
+ worshipAndReligiousUnderstanding.OpinionOnVeil = req.OpinionOnVeil
302
+ worshipAndReligiousUnderstanding.WeeklyReligiousStudies = req.WeeklyReligiousStudies
303
+ worshipAndReligiousUnderstanding.FollowedUstadz = req.FollowedUstadz
304
+
305
+ // Simpan data
306
+ res, err := s.cvRepository.SaveWorshipAndReligiousUnderstanding(ctx, worshipAndReligiousUnderstanding)
307
+ if err != nil {
308
+ return nil, response.HandleGormError(err, "Internal Server Error")
309
+ }
310
+
311
+ return res, nil
312
  }
313
 
314
  func (s *cvService) GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error) {
315
+ res, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, id)
316
+ if err != nil {
317
+ return nil, response.HandleGormError(err, "Data agama dan pemahaman agama tidak ditemukan")
318
+ }
319
+ return res, nil
320
  }
321
 
322
  func (s *cvService) CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error) {
323
+ if err := validation.Validate(req); err != nil {
324
+ return nil, response.HandleValidationError(err)
325
+ }
326
+
327
+ edu := &models.EducationCV{
328
+ AccountID: req.AccountID,
329
+ LastEducation: req.LastEducation,
330
+ EducationInstitute: req.EducationInstitute,
331
+ EducationMajor: req.EducationMajor,
332
+ YearStart: req.YearStart,
333
+ YearGraduate: req.YearGraduate,
334
+ }
335
+
336
+ res, err := s.cvRepository.SaveEducation(ctx, edu)
337
+ if err != nil {
338
+ return nil, response.HandleGormError(err, "Gagal menambahkan data pendidikan")
339
+ }
340
+
341
+ return res, nil
342
  }
343
 
344
  func (s *cvService) UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error) {
345
+ if err := validation.Validate(req); err != nil {
346
+ return nil, response.HandleValidationError(err)
347
+ }
348
+
349
+ edu, err := s.cvRepository.GetEducation(ctx, id)
350
+ if err != nil {
351
+ return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
352
+ }
353
+
354
+ edu.LastEducation = req.LastEducation
355
+ edu.EducationInstitute = req.EducationInstitute
356
+ edu.EducationMajor = req.EducationMajor
357
+ edu.YearStart = req.YearStart
358
+ edu.YearGraduate = req.YearGraduate
359
+
360
+ res, err := s.cvRepository.SaveEducation(ctx, edu)
361
+ if err != nil {
362
+ return nil, response.HandleGormError(err, "Gagal memperbarui data pendidikan")
363
+ }
364
+ return res, nil
365
  }
366
 
367
  func (s *cvService) ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error) {
368
+ return s.cvRepository.ListEducation(ctx, accountID)
369
  }
370
 
371
  func (s *cvService) GetEducation(ctx context.Context, id int64) (*models.EducationCV, error) {
372
+ edu, err := s.cvRepository.GetEducation(ctx, id)
373
+ if err != nil {
374
+ return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
375
+ }
376
+ return edu, nil
377
  }
378
 
379
  func (s *cvService) DeleteEducation(ctx context.Context, id int64) error {
380
+ return s.cvRepository.DeleteEducation(ctx, id)
381
  }
382
 
383
  func (s *cvService) CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error) {
384
+ if err := validation.Validate(req); err != nil {
385
+ return nil, response.HandleValidationError(err)
386
+ }
387
+
388
+ job := &models.JobCV{
389
+ AccountID: req.AccountID,
390
+ InstitutionName: req.InstitutionName,
391
+ CurrentJob: req.CurrentJob,
392
+ YearStartedWorking: req.YearStartedWorking,
393
+ MonthlyIncome: req.MonthlyIncome,
394
+ IncomeSources: req.IncomeSources,
395
+ }
396
+ res, err := s.cvRepository.SaveJob(ctx, job)
397
+ if err != nil {
398
+ return nil, response.HandleGormError(err, "Gagal menambahkan data pekerjaan")
399
+ }
400
+ return res, nil
401
  }
402
 
403
  func (s *cvService) UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error) {
404
+ if err := validation.Validate(req); err != nil {
405
+ return nil, response.HandleValidationError(err)
406
+ }
407
+
408
+ job, err := s.cvRepository.GetJob(ctx, id)
409
+ if err != nil {
410
+ return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
411
+ }
412
+
413
+ job.InstitutionName = req.InstitutionName
414
+ job.CurrentJob = req.CurrentJob
415
+ job.YearStartedWorking = req.YearStartedWorking
416
+ job.MonthlyIncome = req.MonthlyIncome
417
+ job.IncomeSources = req.IncomeSources
418
+
419
+ res, err := s.cvRepository.SaveJob(ctx, job)
420
+ if err != nil {
421
+ return nil, response.HandleGormError(err, "Gagal memperbarui data pekerjaan")
422
+ }
423
+ return res, nil
424
  }
425
 
426
  func (s *cvService) ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error) {
427
+ return s.cvRepository.ListJob(ctx, accountID)
428
  }
429
 
430
  func (s *cvService) GetJob(ctx context.Context, id int64) (*models.JobCV, error) {
431
+ job, err := s.cvRepository.GetJob(ctx, id)
432
+ if err != nil {
433
+ return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
434
+ }
435
+ return job, nil
436
  }
437
 
438
  func (s *cvService) DeleteJob(ctx context.Context, id int64) error {
439
+ return s.cvRepository.DeleteJob(ctx, id)
440
  }
441
 
442
  func (s *cvService) CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error) {
443
+ ach := &models.AchievementCV{
444
+ AccountID: req.AccountID,
445
+ AchievementOrAward: req.AchievementOrAward,
446
+ }
447
+ res, err := s.cvRepository.SaveAchievement(ctx, ach)
448
+ if err != nil {
449
+ return nil, response.HandleGormError(err, "Gagal menambahkan data prestasi")
450
+ }
451
+
452
+ return res, nil
453
  }
454
 
455
  func (s *cvService) UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error) {
456
+ ach, err := s.cvRepository.GetAchievement(ctx, id)
457
+ if err != nil {
458
+ return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
459
+ }
460
 
461
+ ach.AchievementOrAward = req.AchievementOrAward
462
 
463
+ res, err := s.cvRepository.SaveAchievement(ctx, ach)
464
+ if err != nil {
465
+ return nil, response.HandleGormError(err, "Gagal memperbarui data prestasi")
466
+ }
467
 
468
+ return res, nil
469
  }
470
 
471
  func (s *cvService) ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error) {
472
+ return s.cvRepository.ListAchievement(ctx, accountID)
473
  }
474
 
475
  func (s *cvService) GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error) {
476
+ ach, err := s.cvRepository.GetAchievement(ctx, id)
477
+ if err != nil {
478
+ return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
479
+ }
480
+ return ach, nil
481
  }
482
 
483
  func (s *cvService) DeleteAchievement(ctx context.Context, id int64) error {
484
+ return s.cvRepository.DeleteAchievement(ctx, id)
485
  }
space/go.sum CHANGED
@@ -97,6 +97,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
97
  github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
98
  github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
99
  github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
 
 
100
  github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
101
  github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
102
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 
97
  github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
98
  github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
99
  github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
100
+ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
101
+ github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
102
  github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
103
  github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
104
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
space/models/database_orm_model.go CHANGED
@@ -3,6 +3,7 @@ package models
3
  import (
4
  "time"
5
 
 
6
  uuid "github.com/satori/go.uuid"
7
  )
8
 
@@ -241,33 +242,33 @@ type (
241
  }
242
 
243
  WorshipAndReligiousUnderstandingCV struct {
244
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
245
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
246
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
247
- ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
248
- CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
249
- TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
250
- DhuhaPrayer *string `gorm:"column:dhuha_prayer" json:"dhuha_prayer"` // sholat_dhuha
251
- QuranMemorization *string `gorm:"column:quran_memorization" json:"quran_memorization"` // hafalan_alquran
252
- QuranReadingAbility *string `gorm:"column:quran_reading_ability" json:"quran_reading_ability"` // kemampuan_baca_alquran
253
- DaudFasting *string `gorm:"column:daud_fasting" json:"daud_fasting"` // puasa_daud
254
- AyyamulBidhFasting *string `gorm:"column:ayyamul_bidh_fasting" json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
255
- HajjOrUmrah *string `gorm:"column:hajj_or_umrah" json:"hajj_or_umrah"` // ibadah_haji_umroh
256
- ListeningToMusic *string `gorm:"column:listening_to_music" json:"listening_to_music"` // mendengarkan_musik
257
- OpinionOnIkhtilat *string `gorm:"column:opinion_on_ikhtilat" json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
258
- OpinionOnTouchingNonMahram *string `gorm:"column:opinion_on_touching_non_mahram" json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
259
- OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
260
- WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
261
- FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
262
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
263
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
264
  }
265
 
266
  EducationCV struct {
267
  ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
268
  AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
269
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
270
- LastEducation *string `gorm:"column:last_education" json:"last_education"` // pendidikan terakhir
271
  EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
272
  EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
273
  YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
@@ -277,16 +278,16 @@ type (
277
  }
278
 
279
  JobCV struct {
280
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
281
- AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
282
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
283
- InstitutionName *string `gorm:"column:institution_name" json:"institution_name"` // nama instansi
284
- CurrentJob *string `gorm:"column:current_job" json:"current_job"` // pekerjaan saat ini
285
- YearStartedWorking *int `gorm:"column:year_started_working" json:"year_started_working"` // tahun mulai bekerja
286
- MonthlyIncome *string `gorm:"column:monthly_income" json:"monthly_income"` // penghasilan per bulan
287
- IncomeSources *string `gorm:"column:income_sources" json:"income_sources"` // sumber penghasilan
288
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
289
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
290
  }
291
 
292
  AchievementCV struct {
 
3
  import (
4
  "time"
5
 
6
+ "github.com/lib/pq"
7
  uuid "github.com/satori/go.uuid"
8
  )
9
 
 
242
  }
243
 
244
  WorshipAndReligiousUnderstandingCV struct {
245
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
246
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
247
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
248
+ ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
249
+ CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
250
+ TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
251
+ DhuhaPrayer *string `gorm:"column:dhuha_prayer" json:"dhuha_prayer"` // sholat_dhuha
252
+ QuranMemorization *string `gorm:"column:quran_memorization" json:"quran_memorization"` // hafalan_alquran
253
+ QuranReadingAbility *string `gorm:"column:quran_reading_ability" json:"quran_reading_ability"` // kemampuan_baca_alquran
254
+ DaudFasting *string `gorm:"column:daud_fasting" json:"daud_fasting"` // puasa_daud
255
+ AyyamulBidhFasting *string `gorm:"column:ayyamul_bidh_fasting" json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
256
+ HajjOrUmrah pq.StringArray `gorm:"column:hajj_or_umrah;type:varchar(255)[]" json:"hajj_or_umrah"` // ibadah_haji_umroh
257
+ ListeningToMusic *string `gorm:"column:listening_to_music" json:"listening_to_music"` // mendengarkan_musik
258
+ OpinionOnIkhtilat *string `gorm:"column:opinion_on_ikhtilat" json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
259
+ OpinionOnTouchingNonMahram *string `gorm:"column:opinion_on_touching_non_mahram" json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
260
+ OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
261
+ WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
262
+ FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
263
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
264
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
265
  }
266
 
267
  EducationCV struct {
268
  ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
269
  AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
270
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
271
+ LastEducation *string `gorm:"column:last_education" json:"last_education" validate:""` // pendidikan terakhir
272
  EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
273
  EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
274
  YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
 
278
  }
279
 
280
  JobCV struct {
281
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
282
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
283
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
284
+ InstitutionName *string `gorm:"column:institution_name" json:"institution_name"` // nama instansi
285
+ CurrentJob *string `gorm:"column:current_job" json:"current_job"` // pekerjaan saat ini
286
+ YearStartedWorking *int `gorm:"column:year_started_working" json:"year_started_working"` // tahun mulai bekerja
287
+ MonthlyIncome *string `gorm:"column:monthly_income" json:"monthly_income"` // penghasilan per bulan
288
+ IncomeSources pq.StringArray `gorm:"column:income_sources;type:varchar(255)[]" json:"income_sources"` // sumber penghasilan
289
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
290
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
291
  }
292
 
293
  AchievementCV struct {
space/models/request_model.go CHANGED
@@ -1,6 +1,9 @@
1
  package models
2
 
3
- import "time"
 
 
 
4
 
5
  type LoginRequest struct {
6
  Email string `json:"email" binding:"required"`
@@ -55,91 +58,91 @@ type AnswerQuizRequest struct {
55
  type (
56
  PersonalityAndPreferenceCVRequest struct {
57
  AccountID int64 `json:"-"`
58
- PositiveTraits *string `json:"positive_traits"` // sifat positif
59
- NegativeTraits *string `json:"negative_traits"` // sifat negatif
60
- Hobbies *string `json:"hobbies"` // hobi
61
- LifeGoals *string `json:"life_goals"` // target hidup
62
- DailyActivities *string `json:"daily_activities"` // kegiatan sehari-hari
63
- LeisureActivities *string `json:"leisure_activities"` // kegiatan waktu luang
64
- Likes *string `json:"likes"` // hal yang disukai
65
- Dislikes *string `json:"dislikes"` // hal yang tidak disukai
66
- StressHandling *string `json:"stress_handling"` // cara mengatasi stres
67
- AngerTriggers *string `json:"anger_triggers"` // pemicu amarah
68
- FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
69
- CanCook *bool `json:"can_cook"` // bisa memasak
70
- TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
71
- MonthlyExpenses *string `json:"monthly_expenses"` // pengeluaran per bulan
72
  }
73
 
74
  FamilyMemberRequest struct {
75
  AccountID int64 `json:"-"`
76
- Role *string `json:"role"` // Peran dalam keluarga
77
- Status *string `json:"status"` // Status (Hidup, Wafat)
78
- Religion *string `json:"religion"` // Agama
79
- Job *string `json:"job"` // Pekerjaan
80
- LastEducation *string `json:"last_education"` // Pendidikan terakhir
81
- Age *int `json:"age"` // Usia
82
  }
83
 
84
  PhysicalAndHealthRequest struct {
85
  AccountID int64 `json:"-"`
86
- HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
87
- WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
88
- BodyShape *string `json:"body_shape"` // Bentuk tubuh
89
- SkinColor *string `json:"skin_color"` // Warna kulit
90
- HairType *string `json:"hair_type"` // Tipe rambut
91
- MedicalHistory *string `json:"medical_history"` // Riwayat penyakit
92
- PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
93
- PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
94
  }
95
 
96
  AccountDetailsRequest struct {
97
  AccountID int64 `json:"-"`
98
  FullName *string `json:"full_name"`
99
- Gender *string `json:"gender"`
100
  DateOfBirth *time.Time `json:"date_of_birth"`
101
  PlaceOfBirth *string `json:"place_of_birth"`
102
  Domicile *string `json:"domicile"`
103
- MaritalStatus *string `json:"marital_status"`
104
- LastEducation *string `json:"last_education"`
105
  LastJob *string `json:"last_job"`
106
  }
107
 
108
  WorshipAndReligiousUnderstandingRequest struct {
109
- AccountID int64 `json:"-"`
110
- ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
111
- CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
112
- TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
113
- DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
114
- QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
115
- QuranReadingAbility *string `json:"quran_reading_ability"` // kemampuan_baca_alquran
116
- DaudFasting *string `json:"daud_fasting"` // puasa_daud
117
- AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
118
- HajjOrUmrah *string `json:"hajj_or_umrah"` // ibadah_haji_umroh
119
- ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
120
- OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
121
- OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
122
- OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
123
- WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
124
- FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
125
  }
126
 
127
  EducationRequest struct {
128
  AccountID int64 `json:"account_id"`
129
- LastEducation *string `json:"last_education"` // pendidikan terakhir
130
- EducationInstitute *string `json:"education_institute"` // institusi pendidikan
131
- EducationMajor *string `json:"education_major"` // jurusan pendidikan
132
- YearStart *int `json:"year_start"` // tahun masuk
133
- YearGraduate *int `json:"year_graduate"` // tahun lulus
134
  }
135
 
136
  JobRequest struct {
137
- AccountID int64 `json:"account_id"`
138
- InstitutionName *string `json:"institution_name"` // nama instansi
139
- CurrentJob *string `json:"current_job"` // pekerjaan saat ini
140
- YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
141
- MonthlyIncome *string `json:"monthly_income"` // penghasilan per bulan
142
- IncomeSources *string `json:"income_sources"` // sumber penghasilan
143
  }
144
 
145
  AchievementRequest struct {
 
1
  package models
2
 
3
+ import (
4
+ "github.com/lib/pq"
5
+ "time"
6
+ )
7
 
8
  type LoginRequest struct {
9
  Email string `json:"email" binding:"required"`
 
58
  type (
59
  PersonalityAndPreferenceCVRequest struct {
60
  AccountID int64 `json:"-"`
61
+ PositiveTraits *string `json:"positive_traits"` // sifat positif
62
+ NegativeTraits *string `json:"negative_traits"` // sifat negatif
63
+ Hobbies *string `json:"hobbies"` // hobi
64
+ LifeGoals *string `json:"life_goals"` // target hidup
65
+ DailyActivities *string `json:"daily_activities"` // kegiatan sehari-hari
66
+ LeisureActivities *string `json:"leisure_activities"` // kegiatan waktu luang
67
+ Likes *string `json:"likes"` // hal yang disukai
68
+ Dislikes *string `json:"dislikes"` // hal yang tidak disukai
69
+ StressHandling *string `json:"stress_handling"` // cara mengatasi stres
70
+ AngerTriggers *string `json:"anger_triggers"` // pemicu amarah
71
+ FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
72
+ CanCook *bool `json:"can_cook"` // bisa memasak
73
+ TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
74
+ MonthlyExpenses *string `json:"monthly_expenses" validate:"monthly_expenses"` // pengeluaran per bulan
75
  }
76
 
77
  FamilyMemberRequest struct {
78
  AccountID int64 `json:"-"`
79
+ Role *string `json:"role" validate:"family_role"` // Peran dalam keluarga
80
+ Status *string `json:"status" validate:"life_status"` // Status (Hidup, Wafat)
81
+ Religion *string `json:"religion" validate:"religion"` // Agama
82
+ Job *string `json:"job"` // Pekerjaan
83
+ LastEducation *string `json:"last_education" validate:"last_education"` // Pendidikan terakhir
84
+ Age *int `json:"age"` // Usia
85
  }
86
 
87
  PhysicalAndHealthRequest struct {
88
  AccountID int64 `json:"-"`
89
+ HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
90
+ WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
91
+ BodyShape *string `json:"body_shape" validate:"body_shape"` // Bentuk tubuh
92
+ SkinColor *string `json:"skin_color" validate:"skin_color"` // Warna kulit
93
+ HairType *string `json:"hair_type" validate:"hair_type"` // Tipe rambut
94
+ MedicalHistory *string `json:"medical_history"` // Riwayat penyakit
95
+ PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
96
+ PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
97
  }
98
 
99
  AccountDetailsRequest struct {
100
  AccountID int64 `json:"-"`
101
  FullName *string `json:"full_name"`
102
+ Gender *string `json:"gender" validate:"gender"`
103
  DateOfBirth *time.Time `json:"date_of_birth"`
104
  PlaceOfBirth *string `json:"place_of_birth"`
105
  Domicile *string `json:"domicile"`
106
+ MaritalStatus *string `json:"marital_status" validate:"marital_status"`
107
+ LastEducation *string `json:"last_education" validate:"last_education"`
108
  LastJob *string `json:"last_job"`
109
  }
110
 
111
  WorshipAndReligiousUnderstandingRequest struct {
112
+ AccountID int64 `json:"-"`
113
+ ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
114
+ CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
115
+ TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
116
+ DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
117
+ QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
118
+ QuranReadingAbility *string `json:"quran_reading_ability" validate:"quran_reading_ability"` // kemampuan_baca_alquran
119
+ DaudFasting *string `json:"daud_fasting"` // puasa_daud
120
+ AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
121
+ HajjOrUmrah pq.StringArray `json:"hajj_or_umrah"` // ibadah_haji_umroh
122
+ ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
123
+ OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
124
+ OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
125
+ OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
126
+ WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
127
+ FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
128
  }
129
 
130
  EducationRequest struct {
131
  AccountID int64 `json:"account_id"`
132
+ LastEducation *string `json:"last_education" validate:"last_education"` // pendidikan terakhir
133
+ EducationInstitute *string `json:"education_institute"` // institusi pendidikan
134
+ EducationMajor *string `json:"education_major"` // jurusan pendidikan
135
+ YearStart *int `json:"year_start"` // tahun masuk
136
+ YearGraduate *int `json:"year_graduate"` // tahun lulus
137
  }
138
 
139
  JobRequest struct {
140
+ AccountID int64 `json:"account_id"`
141
+ InstitutionName *string `json:"institution_name"` // nama instansi
142
+ CurrentJob *string `json:"current_job"` // pekerjaan saat ini
143
+ YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
144
+ MonthlyIncome *string `json:"monthly_income" validate:"monthly_income"` // penghasilan per bulan
145
+ IncomeSources pq.StringArray `json:"income_sources"` // sumber penghasilan
146
  }
147
 
148
  AchievementRequest struct {
space/space/controller/quiz/list_quiz_controller.go ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controller
2
+
3
+ import (
4
+ "strconv"
5
+
6
+ "api.qobiltu.id/controller"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func List(c *gin.Context) {
13
+ quizList := services.QuizListService{}
14
+ quizListController := controller.Controller[any, models.Academy, []models.Quiz]{
15
+ Service: &quizList.Service,
16
+ }
17
+ quizListController.HeaderParse(c, func() {
18
+ academy_id, _ := strconv.Atoi(c.Param("academy_id"))
19
+ quizList.Constructor.ID = uint(academy_id)
20
+ quizList.Retrieve()
21
+ quizListController.Response(c)
22
+ })
23
+ }
space/space/go.mod CHANGED
@@ -4,11 +4,15 @@ go 1.24.0
4
 
5
  require (
6
  github.com/gin-gonic/gin v1.10.0
 
 
 
7
  github.com/golang-jwt/jwt/v5 v5.2.1
8
  github.com/gosimple/slug v1.15.0
9
  github.com/hibiken/asynq v0.25.1
10
  github.com/joho/godotenv v1.5.1
11
  github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
 
12
  github.com/redis/go-redis/v9 v9.7.0
13
  github.com/satori/go.uuid v1.2.0
14
  golang.org/x/crypto v0.36.0
@@ -31,9 +35,6 @@ require (
31
  github.com/gin-contrib/sse v1.0.0 // indirect
32
  github.com/go-logr/logr v1.4.2 // indirect
33
  github.com/go-logr/stdr v1.2.2 // indirect
34
- github.com/go-playground/locales v0.14.1 // indirect
35
- github.com/go-playground/universal-translator v0.18.1 // indirect
36
- github.com/go-playground/validator/v10 v10.25.0 // indirect
37
  github.com/goccy/go-json v0.10.5 // indirect
38
  github.com/google/s2a-go v0.1.9 // indirect
39
  github.com/google/uuid v1.6.0 // indirect
 
4
 
5
  require (
6
  github.com/gin-gonic/gin v1.10.0
7
+ github.com/go-playground/locales v0.14.1
8
+ github.com/go-playground/universal-translator v0.18.1
9
+ github.com/go-playground/validator/v10 v10.25.0
10
  github.com/golang-jwt/jwt/v5 v5.2.1
11
  github.com/gosimple/slug v1.15.0
12
  github.com/hibiken/asynq v0.25.1
13
  github.com/joho/godotenv v1.5.1
14
  github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
15
+ github.com/lib/pq v1.10.9
16
  github.com/redis/go-redis/v9 v9.7.0
17
  github.com/satori/go.uuid v1.2.0
18
  golang.org/x/crypto v0.36.0
 
35
  github.com/gin-contrib/sse v1.0.0 // indirect
36
  github.com/go-logr/logr v1.4.2 // indirect
37
  github.com/go-logr/stdr v1.2.2 // indirect
 
 
 
38
  github.com/goccy/go-json v0.10.5 // indirect
39
  github.com/google/s2a-go v0.1.9 // indirect
40
  github.com/google/uuid v1.6.0 // indirect
space/space/router/quiz_route.go CHANGED
@@ -9,6 +9,7 @@ import (
9
  func QuizRoute(router *gin.Engine) {
10
  routerGroup := router.Group("/api/v1/quiz")
11
  {
 
12
  routerGroup.POST("/:academy_id/:quiz_id/attempt", middleware.AuthUser, QuizController.Attempt)
13
  routerGroup.GET("/:academy_id/:quiz_id/question", middleware.AuthUser, QuizController.Question)
14
  routerGroup.PUT("/:academy_id/:quiz_id/choose-answer", middleware.AuthUser, QuizController.Answer)
 
9
  func QuizRoute(router *gin.Engine) {
10
  routerGroup := router.Group("/api/v1/quiz")
11
  {
12
+ routerGroup.GET("/:academy_id/list", middleware.AuthUser, QuizController.List)
13
  routerGroup.POST("/:academy_id/:quiz_id/attempt", middleware.AuthUser, QuizController.Attempt)
14
  routerGroup.GET("/:academy_id/:quiz_id/question", middleware.AuthUser, QuizController.Question)
15
  routerGroup.PUT("/:academy_id/:quiz_id/choose-answer", middleware.AuthUser, QuizController.Answer)
space/space/services/academy_quiz_service.go CHANGED
@@ -2,12 +2,12 @@ package services
2
 
3
  import (
4
  "errors"
5
- "fmt"
6
  "time"
7
 
8
  "api.qobiltu.id/models"
9
  "api.qobiltu.id/repositories"
10
  )
 
11
  type AttemptQuizService struct {
12
  Service[models.Quiz, models.QuizAttempt]
13
  }
@@ -16,6 +16,10 @@ type SubmitQuizService struct {
16
  Service[models.QuizAttempt, models.QuizResultResponse]
17
  }
18
 
 
 
 
 
19
  func AuthorizeQuizwithAcademy(s *AttemptQuizService, next func()) {
20
  academyRepo := repositories.GetAcademyByID(s.Constructor.AcademyID)
21
  s.Error = academyRepo.RowsError
@@ -77,8 +81,8 @@ func (s *AttemptQuizService) Validate(userID uint, next func(latestAttemptRepo r
77
  }
78
  s.Error = errors.Join(allAttemptsRepo.RowsError, quizRepo.RowsError)
79
  CheckUserAttemptLimit(s, allAttemptsRepo, quizRepo, userID, func() {
80
- fmt.Println("accountID", userID)
81
- fmt.Println("quizID", s.Constructor.ID)
82
  latestAttemptRepo := repositories.GetUserLastAttempt(userID, s.Constructor.ID)
83
  if latestAttemptRepo.NoRecord {
84
  s.Exception.DataNotFound = true
@@ -125,6 +129,7 @@ func (s *AttemptQuizService) Create(userID uint) {
125
  }
126
  })
127
  }
 
128
  func (s *SubmitQuizService) Create(userID uint) {
129
  quizAttemptRepo := repositories.GetAttemptById(s.Constructor.ID)
130
  if quizAttemptRepo.NoRecord {
@@ -151,3 +156,15 @@ func (s *SubmitQuizService) Create(userID uint) {
151
 
152
  return
153
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  import (
4
  "errors"
 
5
  "time"
6
 
7
  "api.qobiltu.id/models"
8
  "api.qobiltu.id/repositories"
9
  )
10
+
11
  type AttemptQuizService struct {
12
  Service[models.Quiz, models.QuizAttempt]
13
  }
 
16
  Service[models.QuizAttempt, models.QuizResultResponse]
17
  }
18
 
19
+ type QuizListService struct {
20
+ Service[models.Academy, []models.Quiz]
21
+ }
22
+
23
  func AuthorizeQuizwithAcademy(s *AttemptQuizService, next func()) {
24
  academyRepo := repositories.GetAcademyByID(s.Constructor.AcademyID)
25
  s.Error = academyRepo.RowsError
 
81
  }
82
  s.Error = errors.Join(allAttemptsRepo.RowsError, quizRepo.RowsError)
83
  CheckUserAttemptLimit(s, allAttemptsRepo, quizRepo, userID, func() {
84
+ // fmt.Println("accountID", userID)
85
+ // fmt.Println("quizID", s.Constructor.ID)
86
  latestAttemptRepo := repositories.GetUserLastAttempt(userID, s.Constructor.ID)
87
  if latestAttemptRepo.NoRecord {
88
  s.Exception.DataNotFound = true
 
129
  }
130
  })
131
  }
132
+
133
  func (s *SubmitQuizService) Create(userID uint) {
134
  quizAttemptRepo := repositories.GetAttemptById(s.Constructor.ID)
135
  if quizAttemptRepo.NoRecord {
 
156
 
157
  return
158
  }
159
+
160
+ func (s *QuizListService) Retrieve() {
161
+ quizRepo := repositories.GetQuizbyAcademyId(s.Constructor.ID)
162
+ s.Error = quizRepo.RowsError
163
+ if quizRepo.NoRecord {
164
+ s.Exception.DataNotFound = true
165
+ s.Exception.Message = "There is no quiz with given academy ID!"
166
+ return
167
+ }
168
+ s.Result = quizRepo.Result
169
+ return
170
+ }
space/space/space/config/database_connection_config.go CHANGED
@@ -74,6 +74,9 @@ func AutoMigrateAll(db *gorm.DB) {
74
  &models.FamilyMemberCV{},
75
  &models.PhysicalAndHealthCV{},
76
  &models.WorshipAndReligiousUnderstandingCV{},
 
 
 
77
  )
78
 
79
  if err != nil {
 
74
  &models.FamilyMemberCV{},
75
  &models.PhysicalAndHealthCV{},
76
  &models.WorshipAndReligiousUnderstandingCV{},
77
+ &models.EducationCV{},
78
+ &models.JobCV{},
79
+ &models.AchievementCV{},
80
  )
81
 
82
  if err != nil {
space/space/space/controller/cv/cv_controller.go CHANGED
@@ -28,6 +28,24 @@ type CVController interface {
28
 
29
  SaveWorshipAndReligiousUnderstanding(ctx *gin.Context)
30
  GetWorshipAndReligiousUnderstanding(ctx *gin.Context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
 
33
  type cvController struct {
@@ -265,3 +283,273 @@ func (c *cvController) GetWorshipAndReligiousUnderstanding(ctx *gin.Context) {
265
 
266
  response.HandleSuccess(ctx, http.StatusOK, "Get worship and religious understanding success", res, nil)
267
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  SaveWorshipAndReligiousUnderstanding(ctx *gin.Context)
30
  GetWorshipAndReligiousUnderstanding(ctx *gin.Context)
31
+
32
+ CreateEducation(ctx *gin.Context)
33
+ UpdateEducation(ctx *gin.Context)
34
+ ListEducation(ctx *gin.Context)
35
+ GetEducation(ctx *gin.Context)
36
+ DeleteEducation(ctx *gin.Context)
37
+
38
+ CreateJob(ctx *gin.Context)
39
+ UpdateJob(ctx *gin.Context)
40
+ ListJob(ctx *gin.Context)
41
+ GetJob(ctx *gin.Context)
42
+ DeleteJob(ctx *gin.Context)
43
+
44
+ CreateAchievement(ctx *gin.Context)
45
+ UpdateAchievement(ctx *gin.Context)
46
+ ListAchievement(ctx *gin.Context)
47
+ GetAchievement(ctx *gin.Context)
48
+ DeleteAchievement(ctx *gin.Context)
49
  }
50
 
51
  type cvController struct {
 
283
 
284
  response.HandleSuccess(ctx, http.StatusOK, "Get worship and religious understanding success", res, nil)
285
  }
286
+
287
+ // --- Education ---
288
+ func (c *cvController) CreateEducation(ctx *gin.Context) {
289
+ var req models.EducationRequest
290
+ if err := ctx.ShouldBindJSON(&req); err != nil {
291
+ response.HandleError(ctx, err)
292
+ return
293
+ }
294
+
295
+ accountData := middleware.GetAccountData(ctx)
296
+ req.AccountID = int64(accountData.UserID)
297
+
298
+ res, err := c.cvService.CreateEducation(ctx, &req)
299
+ if err != nil {
300
+ response.HandleError(ctx, err)
301
+ return
302
+ }
303
+
304
+ response.HandleSuccess(ctx, http.StatusOK, "Education created successfully", res, nil)
305
+ }
306
+
307
+ func (c *cvController) UpdateEducation(ctx *gin.Context) {
308
+ idStr := ctx.Param("id")
309
+ id, err := strconv.ParseInt(idStr, 10, 64)
310
+ if err != nil {
311
+ response.HandleError(ctx, err)
312
+ return
313
+ }
314
+
315
+ var req models.EducationRequest
316
+ if err := ctx.ShouldBindJSON(&req); err != nil {
317
+ response.HandleError(ctx, err)
318
+ return
319
+ }
320
+
321
+ res, err := c.cvService.UpdateEducation(ctx, id, &req)
322
+ if err != nil {
323
+ response.HandleError(ctx, err)
324
+ return
325
+ }
326
+
327
+ response.HandleSuccess(ctx, http.StatusOK, "Education updated successfully", res, nil)
328
+ }
329
+
330
+ func (c *cvController) ListEducation(ctx *gin.Context) {
331
+ accountData := middleware.GetAccountData(ctx)
332
+ accountID := int64(accountData.UserID)
333
+
334
+ res, err := c.cvService.ListEducation(ctx, accountID)
335
+ if err != nil {
336
+ response.HandleError(ctx, err)
337
+ return
338
+ }
339
+
340
+ response.HandleSuccess(ctx, http.StatusOK, "List of education retrieved successfully", res, nil)
341
+ }
342
+
343
+ func (c *cvController) GetEducation(ctx *gin.Context) {
344
+ idStr := ctx.Param("id")
345
+ id, err := strconv.ParseInt(idStr, 10, 64)
346
+ if err != nil {
347
+ response.HandleError(ctx, err)
348
+ return
349
+ }
350
+
351
+ res, err := c.cvService.GetEducation(ctx, id)
352
+ if err != nil {
353
+ response.HandleError(ctx, err)
354
+ return
355
+ }
356
+
357
+ response.HandleSuccess(ctx, http.StatusOK, "Get education success", res, nil)
358
+ }
359
+
360
+ func (c *cvController) DeleteEducation(ctx *gin.Context) {
361
+ idStr := ctx.Param("id")
362
+ id, err := strconv.ParseInt(idStr, 10, 64)
363
+ if err != nil {
364
+ response.HandleError(ctx, err)
365
+ return
366
+ }
367
+
368
+ err = c.cvService.DeleteEducation(ctx, id)
369
+ if err != nil {
370
+ response.HandleError(ctx, err)
371
+ return
372
+ }
373
+
374
+ response.HandleSuccess(ctx, http.StatusOK, "Education deleted successfully", nil, nil)
375
+ }
376
+
377
+ // --- Job ---
378
+ func (c *cvController) CreateJob(ctx *gin.Context) {
379
+ var req models.JobRequest
380
+ if err := ctx.ShouldBindJSON(&req); err != nil {
381
+ response.HandleError(ctx, err)
382
+ return
383
+ }
384
+
385
+ accountData := middleware.GetAccountData(ctx)
386
+ req.AccountID = int64(accountData.UserID)
387
+
388
+ res, err := c.cvService.CreateJob(ctx, &req)
389
+ if err != nil {
390
+ response.HandleError(ctx, err)
391
+ return
392
+ }
393
+
394
+ response.HandleSuccess(ctx, http.StatusOK, "Job created successfully", res, nil)
395
+ }
396
+
397
+ func (c *cvController) UpdateJob(ctx *gin.Context) {
398
+ idStr := ctx.Param("id")
399
+ id, err := strconv.ParseInt(idStr, 10, 64)
400
+ if err != nil {
401
+ response.HandleError(ctx, err)
402
+ return
403
+ }
404
+
405
+ var req models.JobRequest
406
+ if err := ctx.ShouldBindJSON(&req); err != nil {
407
+ response.HandleError(ctx, err)
408
+ return
409
+ }
410
+
411
+ res, err := c.cvService.UpdateJob(ctx, id, &req)
412
+ if err != nil {
413
+ response.HandleError(ctx, err)
414
+ return
415
+ }
416
+
417
+ response.HandleSuccess(ctx, http.StatusOK, "Job updated successfully", res, nil)
418
+ }
419
+
420
+ func (c *cvController) ListJob(ctx *gin.Context) {
421
+ accountData := middleware.GetAccountData(ctx)
422
+ accountID := int64(accountData.UserID)
423
+
424
+ res, err := c.cvService.ListJob(ctx, accountID)
425
+ if err != nil {
426
+ response.HandleError(ctx, err)
427
+ return
428
+ }
429
+
430
+ response.HandleSuccess(ctx, http.StatusOK, "List of jobs retrieved successfully", res, nil)
431
+ }
432
+
433
+ func (c *cvController) GetJob(ctx *gin.Context) {
434
+ idStr := ctx.Param("id")
435
+ id, err := strconv.ParseInt(idStr, 10, 64)
436
+ if err != nil {
437
+ response.HandleError(ctx, err)
438
+ return
439
+ }
440
+
441
+ res, err := c.cvService.GetJob(ctx, id)
442
+ if err != nil {
443
+ response.HandleError(ctx, err)
444
+ return
445
+ }
446
+
447
+ response.HandleSuccess(ctx, http.StatusOK, "Get job success", res, nil)
448
+ }
449
+
450
+ func (c *cvController) DeleteJob(ctx *gin.Context) {
451
+ idStr := ctx.Param("id")
452
+ id, err := strconv.ParseInt(idStr, 10, 64)
453
+ if err != nil {
454
+ response.HandleError(ctx, err)
455
+ return
456
+ }
457
+
458
+ err = c.cvService.DeleteJob(ctx, id)
459
+ if err != nil {
460
+ response.HandleError(ctx, err)
461
+ return
462
+ }
463
+
464
+ response.HandleSuccess(ctx, http.StatusOK, "Job deleted successfully", nil, nil)
465
+ }
466
+
467
+ // --- Achievement ---
468
+ func (c *cvController) CreateAchievement(ctx *gin.Context) {
469
+ var req models.AchievementRequest
470
+ if err := ctx.ShouldBindJSON(&req); err != nil {
471
+ response.HandleError(ctx, err)
472
+ return
473
+ }
474
+
475
+ accountData := middleware.GetAccountData(ctx)
476
+ req.AccountID = int64(accountData.UserID)
477
+
478
+ res, err := c.cvService.CreateAchievement(ctx, &req)
479
+ if err != nil {
480
+ response.HandleError(ctx, err)
481
+ return
482
+ }
483
+
484
+ response.HandleSuccess(ctx, http.StatusOK, "Achievement created successfully", res, nil)
485
+ }
486
+
487
+ func (c *cvController) UpdateAchievement(ctx *gin.Context) {
488
+ idStr := ctx.Param("id")
489
+ id, err := strconv.ParseInt(idStr, 10, 64)
490
+ if err != nil {
491
+ response.HandleError(ctx, err)
492
+ return
493
+ }
494
+
495
+ var req models.AchievementRequest
496
+ if err := ctx.ShouldBindJSON(&req); err != nil {
497
+ response.HandleError(ctx, err)
498
+ return
499
+ }
500
+
501
+ res, err := c.cvService.UpdateAchievement(ctx, id, &req)
502
+ if err != nil {
503
+ response.HandleError(ctx, err)
504
+ return
505
+ }
506
+
507
+ response.HandleSuccess(ctx, http.StatusOK, "Achievement updated successfully", res, nil)
508
+ }
509
+
510
+ func (c *cvController) ListAchievement(ctx *gin.Context) {
511
+ accountData := middleware.GetAccountData(ctx)
512
+ accountID := int64(accountData.UserID)
513
+
514
+ res, err := c.cvService.ListAchievement(ctx, accountID)
515
+ if err != nil {
516
+ response.HandleError(ctx, err)
517
+ return
518
+ }
519
+
520
+ response.HandleSuccess(ctx, http.StatusOK, "List of achievements retrieved successfully", res, nil)
521
+ }
522
+
523
+ func (c *cvController) GetAchievement(ctx *gin.Context) {
524
+ idStr := ctx.Param("id")
525
+ id, err := strconv.ParseInt(idStr, 10, 64)
526
+ if err != nil {
527
+ response.HandleError(ctx, err)
528
+ return
529
+ }
530
+
531
+ res, err := c.cvService.GetAchievement(ctx, id)
532
+ if err != nil {
533
+ response.HandleError(ctx, err)
534
+ return
535
+ }
536
+
537
+ response.HandleSuccess(ctx, http.StatusOK, "Get achievement success", res, nil)
538
+ }
539
+
540
+ func (c *cvController) DeleteAchievement(ctx *gin.Context) {
541
+ idStr := ctx.Param("id")
542
+ id, err := strconv.ParseInt(idStr, 10, 64)
543
+ if err != nil {
544
+ response.HandleError(ctx, err)
545
+ return
546
+ }
547
+
548
+ err = c.cvService.DeleteAchievement(ctx, id)
549
+ if err != nil {
550
+ response.HandleError(ctx, err)
551
+ return
552
+ }
553
+
554
+ response.HandleSuccess(ctx, http.StatusOK, "Achievement deleted successfully", nil, nil)
555
+ }
space/space/space/models/database_orm_model.go CHANGED
@@ -155,6 +155,7 @@ type Question struct {
155
  type Quiz struct {
156
  ID uint `gorm:"primaryKey" json:"id"`
157
  AcademyID uint `json:"academy_id"`
 
158
  Title string `json:"title"`
159
  Description string `json:"description"`
160
  AttemptLimit int `json:"attempt_limit"`
@@ -261,6 +262,41 @@ type (
261
  CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
262
  UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
263
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  )
265
 
266
  // Gorm table name settings
@@ -290,3 +326,6 @@ func (PhysicalAndHealthCV) TableName() string { return "physical_and_heal
290
  func (WorshipAndReligiousUnderstandingCV) TableName() string {
291
  return "worship_and_religious_understanding_cv"
292
  }
 
 
 
 
155
  type Quiz struct {
156
  ID uint `gorm:"primaryKey" json:"id"`
157
  AcademyID uint `json:"academy_id"`
158
+ Slug string `json:"slug" gorm:"uniqueIndex" `
159
  Title string `json:"title"`
160
  Description string `json:"description"`
161
  AttemptLimit int `json:"attempt_limit"`
 
262
  CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
263
  UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
264
  }
265
+
266
+ EducationCV struct {
267
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
268
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
269
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
270
+ LastEducation *string `gorm:"column:last_education" json:"last_education"` // pendidikan terakhir
271
+ EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
272
+ EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
273
+ YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
274
+ YearGraduate *int `gorm:"column:year_graduate" json:"year_graduate"` // tahun lulus
275
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
276
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
277
+ }
278
+
279
+ JobCV struct {
280
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
281
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
282
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
283
+ InstitutionName *string `gorm:"column:institution_name" json:"institution_name"` // nama instansi
284
+ CurrentJob *string `gorm:"column:current_job" json:"current_job"` // pekerjaan saat ini
285
+ YearStartedWorking *int `gorm:"column:year_started_working" json:"year_started_working"` // tahun mulai bekerja
286
+ MonthlyIncome *string `gorm:"column:monthly_income" json:"monthly_income"` // penghasilan per bulan
287
+ IncomeSources *string `gorm:"column:income_sources" json:"income_sources"` // sumber penghasilan
288
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
289
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
290
+ }
291
+
292
+ AchievementCV struct {
293
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
294
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
295
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
296
+ AchievementOrAward *string `gorm:"column:achievement_or_award" json:"achievement_or_award"` // prestasi atau penghargaan
297
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
298
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
299
+ }
300
  )
301
 
302
  // Gorm table name settings
 
326
  func (WorshipAndReligiousUnderstandingCV) TableName() string {
327
  return "worship_and_religious_understanding_cv"
328
  }
329
+ func (EducationCV) TableName() string { return "education_cv" }
330
+ func (JobCV) TableName() string { return "job_cv" }
331
+ func (AchievementCV) TableName() string { return "achievement_cv" }
space/space/space/models/request_model.go CHANGED
@@ -123,4 +123,27 @@ type (
123
  WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
124
  FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
125
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  )
 
123
  WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
124
  FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
125
  }
126
+
127
+ EducationRequest struct {
128
+ AccountID int64 `json:"account_id"`
129
+ LastEducation *string `json:"last_education"` // pendidikan terakhir
130
+ EducationInstitute *string `json:"education_institute"` // institusi pendidikan
131
+ EducationMajor *string `json:"education_major"` // jurusan pendidikan
132
+ YearStart *int `json:"year_start"` // tahun masuk
133
+ YearGraduate *int `json:"year_graduate"` // tahun lulus
134
+ }
135
+
136
+ JobRequest struct {
137
+ AccountID int64 `json:"account_id"`
138
+ InstitutionName *string `json:"institution_name"` // nama instansi
139
+ CurrentJob *string `json:"current_job"` // pekerjaan saat ini
140
+ YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
141
+ MonthlyIncome *string `json:"monthly_income"` // penghasilan per bulan
142
+ IncomeSources *string `json:"income_sources"` // sumber penghasilan
143
+ }
144
+
145
+ AchievementRequest struct {
146
+ AccountID int64 `json:"account_id"`
147
+ AchievementOrAward *string `json:"achievement_or_award"` // prestasi atau penghargaan
148
+ }
149
  )
space/space/space/repositories/cv_repository.go CHANGED
@@ -23,6 +23,21 @@ type CVRepository interface {
23
 
24
  SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error)
25
  GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
  type cvRepository struct {
@@ -124,3 +139,105 @@ func (r *cvRepository) GetWorshipAndReligiousUnderstandingByAccountID(ctx contex
124
  }
125
  return &worshipAndReligiousUnderstanding, nil
126
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error)
25
  GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error)
26
+
27
+ SaveEducation(ctx context.Context, req *models.EducationCV) (*models.EducationCV, error)
28
+ ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error)
29
+ GetEducation(ctx context.Context, id int64) (*models.EducationCV, error)
30
+ DeleteEducation(ctx context.Context, id int64) error
31
+
32
+ SaveJob(ctx context.Context, req *models.JobCV) (*models.JobCV, error)
33
+ ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error)
34
+ GetJob(ctx context.Context, id int64) (*models.JobCV, error)
35
+ DeleteJob(ctx context.Context, id int64) error
36
+
37
+ SaveAchievement(ctx context.Context, req *models.AchievementCV) (*models.AchievementCV, error)
38
+ ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error)
39
+ GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error)
40
+ DeleteAchievement(ctx context.Context, id int64) error
41
  }
42
 
43
  type cvRepository struct {
 
139
  }
140
  return &worshipAndReligiousUnderstanding, nil
141
  }
142
+
143
+ // SaveEducation menyimpan atau memperbarui data pendidikan ke database
144
+ func (r *cvRepository) SaveEducation(ctx context.Context, req *models.EducationCV) (*models.EducationCV, error) {
145
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
146
+ return req, err
147
+ }
148
+ return req, nil
149
+ }
150
+
151
+ // ListEducation mengambil daftar data pendidikan berdasarkan account_id
152
+ func (r *cvRepository) ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error) {
153
+ var educations []models.EducationCV
154
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&educations).Error; err != nil {
155
+ return nil, err
156
+ }
157
+ return educations, nil
158
+ }
159
+
160
+ // GetEducation mengambil satu data pendidikan berdasarkan id
161
+ func (r *cvRepository) GetEducation(ctx context.Context, id int64) (*models.EducationCV, error) {
162
+ var education models.EducationCV
163
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&education).Error; err != nil {
164
+ return nil, err
165
+ }
166
+ return &education, nil
167
+ }
168
+
169
+ // DeleteEducation menghapus data pendidikan berdasarkan id
170
+ func (r *cvRepository) DeleteEducation(ctx context.Context, id int64) error {
171
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.EducationCV{}).Error; err != nil {
172
+ return err
173
+ }
174
+ return nil
175
+ }
176
+
177
+ // SaveJob menyimpan atau memperbarui data pekerjaan ke database
178
+ func (r *cvRepository) SaveJob(ctx context.Context, req *models.JobCV) (*models.JobCV, error) {
179
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
180
+ return req, err
181
+ }
182
+ return req, nil
183
+ }
184
+
185
+ // ListJob mengambil daftar data pekerjaan berdasarkan account_id
186
+ func (r *cvRepository) ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error) {
187
+ var jobs []models.JobCV
188
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&jobs).Error; err != nil {
189
+ return nil, err
190
+ }
191
+ return jobs, nil
192
+ }
193
+
194
+ // GetJob mengambil satu data pekerjaan berdasarkan id
195
+ func (r *cvRepository) GetJob(ctx context.Context, id int64) (*models.JobCV, error) {
196
+ var job models.JobCV
197
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&job).Error; err != nil {
198
+ return nil, err
199
+ }
200
+ return &job, nil
201
+ }
202
+
203
+ // DeleteJob menghapus data pekerjaan berdasarkan id
204
+ func (r *cvRepository) DeleteJob(ctx context.Context, id int64) error {
205
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.JobCV{}).Error; err != nil {
206
+ return err
207
+ }
208
+ return nil
209
+ }
210
+
211
+ // SaveAchievement menyimpan atau memperbarui data prestasi ke database
212
+ func (r *cvRepository) SaveAchievement(ctx context.Context, req *models.AchievementCV) (*models.AchievementCV, error) {
213
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
214
+ return req, err
215
+ }
216
+ return req, nil
217
+ }
218
+
219
+ // ListAchievement mengambil daftar data prestasi berdasarkan account_id
220
+ func (r *cvRepository) ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error) {
221
+ var achievements []models.AchievementCV
222
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&achievements).Error; err != nil {
223
+ return nil, err
224
+ }
225
+ return achievements, nil
226
+ }
227
+
228
+ // GetAchievement mengambil satu data prestasi berdasarkan id
229
+ func (r *cvRepository) GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error) {
230
+ var achievement models.AchievementCV
231
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&achievement).Error; err != nil {
232
+ return nil, err
233
+ }
234
+ return &achievement, nil
235
+ }
236
+
237
+ // DeleteAchievement menghapus data prestasi berdasarkan id
238
+ func (r *cvRepository) DeleteAchievement(ctx context.Context, id int64) error {
239
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.AchievementCV{}).Error; err != nil {
240
+ return err
241
+ }
242
+ return nil
243
+ }
space/space/space/router/cv_route.go CHANGED
@@ -22,5 +22,23 @@ func (s *Server) CVRoute() {
22
 
23
  routerGroup.POST("/worship-and-religious-understandings", s.cvController.SaveWorshipAndReligiousUnderstanding)
24
  routerGroup.GET("/worship-and-religious-understandings", s.cvController.GetWorshipAndReligiousUnderstanding)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
  }
 
22
 
23
  routerGroup.POST("/worship-and-religious-understandings", s.cvController.SaveWorshipAndReligiousUnderstanding)
24
  routerGroup.GET("/worship-and-religious-understandings", s.cvController.GetWorshipAndReligiousUnderstanding)
25
+
26
+ routerGroup.POST("/educations", s.cvController.CreateEducation)
27
+ routerGroup.GET("/educations", s.cvController.ListEducation)
28
+ routerGroup.GET("/educations/:id", s.cvController.GetEducation)
29
+ routerGroup.PUT("/educations/:id", s.cvController.UpdateEducation)
30
+ routerGroup.DELETE("/educations/:id", s.cvController.DeleteEducation)
31
+
32
+ routerGroup.POST("/jobs", s.cvController.CreateJob)
33
+ routerGroup.GET("/jobs", s.cvController.ListJob)
34
+ routerGroup.GET("/jobs/:id", s.cvController.GetJob)
35
+ routerGroup.PUT("/jobs/:id", s.cvController.UpdateJob)
36
+ routerGroup.DELETE("/jobs/:id", s.cvController.DeleteJob)
37
+
38
+ routerGroup.POST("/achievements", s.cvController.CreateAchievement)
39
+ routerGroup.GET("/achievements", s.cvController.ListAchievement)
40
+ routerGroup.GET("/achievements/:id", s.cvController.GetAchievement)
41
+ routerGroup.PUT("/achievements/:id", s.cvController.UpdateAchievement)
42
+ routerGroup.DELETE("/achievements/:id", s.cvController.DeleteAchievement)
43
  }
44
  }
space/space/space/services/cv_service.go CHANGED
@@ -27,6 +27,24 @@ type CVService interface {
27
 
28
  SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
29
  GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
 
32
  type cvService struct {
@@ -275,3 +293,152 @@ func (s *cvService) GetWorshipAndReligiousUnderstanding(ctx context.Context, id
275
  }
276
  return res, nil
277
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
29
  GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error)
30
+
31
+ CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error)
32
+ UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error)
33
+ ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error)
34
+ GetEducation(ctx context.Context, id int64) (*models.EducationCV, error)
35
+ DeleteEducation(ctx context.Context, id int64) error
36
+
37
+ CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error)
38
+ UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error)
39
+ ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error)
40
+ GetJob(ctx context.Context, id int64) (*models.JobCV, error)
41
+ DeleteJob(ctx context.Context, id int64) error
42
+
43
+ CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error)
44
+ UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error)
45
+ ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error)
46
+ GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error)
47
+ DeleteAchievement(ctx context.Context, id int64) error
48
  }
49
 
50
  type cvService struct {
 
293
  }
294
  return res, nil
295
  }
296
+
297
+ func (s *cvService) CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error) {
298
+ edu := &models.EducationCV{
299
+ AccountID: req.AccountID,
300
+ LastEducation: req.LastEducation,
301
+ EducationInstitute: req.EducationInstitute,
302
+ EducationMajor: req.EducationMajor,
303
+ YearStart: req.YearStart,
304
+ YearGraduate: req.YearGraduate,
305
+ }
306
+
307
+ res, err := s.cvRepository.SaveEducation(ctx, edu)
308
+ if err != nil {
309
+ return nil, response.HandleGormError(err, "Gagal menambahkan data pendidikan")
310
+ }
311
+
312
+ return res, nil
313
+ }
314
+
315
+ func (s *cvService) UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error) {
316
+ edu, err := s.cvRepository.GetEducation(ctx, id)
317
+ if err != nil {
318
+ return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
319
+ }
320
+
321
+ edu.LastEducation = req.LastEducation
322
+ edu.EducationInstitute = req.EducationInstitute
323
+ edu.EducationMajor = req.EducationMajor
324
+ edu.YearStart = req.YearStart
325
+ edu.YearGraduate = req.YearGraduate
326
+
327
+ res, err := s.cvRepository.SaveEducation(ctx, edu)
328
+ if err != nil {
329
+ return nil, response.HandleGormError(err, "Gagal memperbarui data pendidikan")
330
+ }
331
+ return res, nil
332
+ }
333
+
334
+ func (s *cvService) ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error) {
335
+ return s.cvRepository.ListEducation(ctx, accountID)
336
+ }
337
+
338
+ func (s *cvService) GetEducation(ctx context.Context, id int64) (*models.EducationCV, error) {
339
+ edu, err := s.cvRepository.GetEducation(ctx, id)
340
+ if err != nil {
341
+ return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
342
+ }
343
+ return edu, nil
344
+ }
345
+
346
+ func (s *cvService) DeleteEducation(ctx context.Context, id int64) error {
347
+ return s.cvRepository.DeleteEducation(ctx, id)
348
+ }
349
+
350
+ func (s *cvService) CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error) {
351
+ job := &models.JobCV{
352
+ AccountID: req.AccountID,
353
+ InstitutionName: req.InstitutionName,
354
+ CurrentJob: req.CurrentJob,
355
+ YearStartedWorking: req.YearStartedWorking,
356
+ MonthlyIncome: req.MonthlyIncome,
357
+ IncomeSources: req.IncomeSources,
358
+ }
359
+ res, err := s.cvRepository.SaveJob(ctx, job)
360
+ if err != nil {
361
+ return nil, response.HandleGormError(err, "Gagal menambahkan data pekerjaan")
362
+ }
363
+ return res, nil
364
+ }
365
+
366
+ func (s *cvService) UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error) {
367
+ job, err := s.cvRepository.GetJob(ctx, id)
368
+ if err != nil {
369
+ return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
370
+ }
371
+
372
+ job.InstitutionName = req.InstitutionName
373
+ job.CurrentJob = req.CurrentJob
374
+ job.YearStartedWorking = req.YearStartedWorking
375
+ job.MonthlyIncome = req.MonthlyIncome
376
+ job.IncomeSources = req.IncomeSources
377
+
378
+ res, err := s.cvRepository.SaveJob(ctx, job)
379
+ if err != nil {
380
+ return nil, response.HandleGormError(err, "Gagal memperbarui data pekerjaan")
381
+ }
382
+ return res, nil
383
+ }
384
+
385
+ func (s *cvService) ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error) {
386
+ return s.cvRepository.ListJob(ctx, accountID)
387
+ }
388
+
389
+ func (s *cvService) GetJob(ctx context.Context, id int64) (*models.JobCV, error) {
390
+ job, err := s.cvRepository.GetJob(ctx, id)
391
+ if err != nil {
392
+ return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
393
+ }
394
+ return job, nil
395
+ }
396
+
397
+ func (s *cvService) DeleteJob(ctx context.Context, id int64) error {
398
+ return s.cvRepository.DeleteJob(ctx, id)
399
+ }
400
+
401
+ func (s *cvService) CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error) {
402
+ ach := &models.AchievementCV{
403
+ AccountID: req.AccountID,
404
+ AchievementOrAward: req.AchievementOrAward,
405
+ }
406
+ res, err := s.cvRepository.SaveAchievement(ctx, ach)
407
+ if err != nil {
408
+ return nil, response.HandleGormError(err, "Gagal menambahkan data prestasi")
409
+ }
410
+
411
+ return res, nil
412
+ }
413
+
414
+ func (s *cvService) UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error) {
415
+ ach, err := s.cvRepository.GetAchievement(ctx, id)
416
+ if err != nil {
417
+ return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
418
+ }
419
+
420
+ ach.AchievementOrAward = req.AchievementOrAward
421
+
422
+ res, err := s.cvRepository.SaveAchievement(ctx, ach)
423
+ if err != nil {
424
+ return nil, response.HandleGormError(err, "Gagal memperbarui data prestasi")
425
+ }
426
+
427
+ return res, nil
428
+ }
429
+
430
+ func (s *cvService) ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error) {
431
+ return s.cvRepository.ListAchievement(ctx, accountID)
432
+ }
433
+
434
+ func (s *cvService) GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error) {
435
+ ach, err := s.cvRepository.GetAchievement(ctx, id)
436
+ if err != nil {
437
+ return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
438
+ }
439
+ return ach, nil
440
+ }
441
+
442
+ func (s *cvService) DeleteAchievement(ctx context.Context, id int64) error {
443
+ return s.cvRepository.DeleteAchievement(ctx, id)
444
+ }
space/space/space/space/controller/user/user_profile_controller.go CHANGED
@@ -1,21 +1,21 @@
1
- package user
2
-
3
- import (
4
- "api.qobiltu.id/controller"
5
- "api.qobiltu.id/models"
6
- "api.qobiltu.id/services"
7
- "github.com/gin-gonic/gin"
8
- )
9
-
10
- func Profile(c *gin.Context) {
11
- userProfile := services.UserProfileService{}
12
- userProfileController := controller.Controller[any, models.AccountDetails, models.UserProfileResponse]{
13
- Service: &userProfile.Service,
14
- }
15
- userProfileController.HeaderParse(c, func() {
16
- userProfileController.Service.Constructor.AccountID = uint(userProfileController.AccountData.UserID)
17
- userProfile.Retrieve()
18
- userProfileController.Response(c)
19
- },
20
- )
21
- }
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func Profile(c *gin.Context) {
11
+ userProfile := services.UserProfileService{}
12
+ userProfileController := controller.Controller[any, models.AccountDetails, models.UserProfileResponse]{
13
+ Service: &userProfile.Service,
14
+ }
15
+ userProfileController.HeaderParse(c, func() {
16
+ userProfileController.Service.Constructor.AccountID = uint(userProfileController.AccountData.UserID)
17
+ userProfile.Retrieve()
18
+ userProfileController.Response(c)
19
+ },
20
+ )
21
+ }
space/space/space/space/controller/user/user_update_profile_controller.go CHANGED
@@ -1,25 +1,25 @@
1
- package user
2
-
3
- import (
4
- "api.qobiltu.id/controller"
5
- "api.qobiltu.id/models"
6
- "api.qobiltu.id/services"
7
- "github.com/gin-gonic/gin"
8
- )
9
-
10
- func UpdateProfile(c *gin.Context) {
11
- userProfile := services.UserProfileService{}
12
- userUpdateProfileController := controller.Controller[models.AccountDetails, models.AccountDetails, models.UserProfileResponse]{
13
- Service: &userProfile.Service,
14
- }
15
-
16
- userUpdateProfileController.RequestJSON(c, func() {
17
- userUpdateProfileController.Service.Constructor = userUpdateProfileController.Request
18
- userUpdateProfileController.HeaderParse(c, func() {
19
- userUpdateProfileController.Service.Constructor.AccountID = uint(userUpdateProfileController.AccountData.UserID)
20
-
21
- })
22
- userProfile.Update()
23
- },
24
- )
25
- }
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func UpdateProfile(c *gin.Context) {
11
+ userProfile := services.UserProfileService{}
12
+ userUpdateProfileController := controller.Controller[models.AccountDetails, models.AccountDetails, models.UserProfileResponse]{
13
+ Service: &userProfile.Service,
14
+ }
15
+
16
+ userUpdateProfileController.RequestJSON(c, func() {
17
+ userUpdateProfileController.Service.Constructor = userUpdateProfileController.Request
18
+ userUpdateProfileController.HeaderParse(c, func() {
19
+ userUpdateProfileController.Service.Constructor.AccountID = userUpdateProfileController.AccountData.UserID
20
+
21
+ })
22
+ userProfile.Update()
23
+ },
24
+ )
25
+ }
space/space/space/space/repositories/account_repository.go CHANGED
@@ -1,87 +1,87 @@
1
- package repositories
2
-
3
- import (
4
- "api.qobiltu.id/models"
5
- )
6
-
7
- func GetAccountbyEmail(email string) Repository[models.Account, models.Account] {
8
- repo := Construct[models.Account, models.Account](
9
- models.Account{Email: email},
10
- )
11
- repo.Transactions(
12
- WhereGivenConstructor[models.Account, models.Account],
13
- Find[models.Account, models.Account],
14
- )
15
- return *repo
16
- }
17
-
18
- func GetAllAccount() Repository[models.Account, []models.Account] {
19
- repo := Construct[models.Account, []models.Account](
20
- models.Account{},
21
- )
22
- repo.Transactions(
23
- Find[models.Account, []models.Account],
24
- )
25
- return *repo
26
- }
27
- func GetAccountById(accountId uint) Repository[models.Account, models.Account] {
28
- repo := Construct[models.Account, models.Account](
29
- models.Account{Id: accountId},
30
- )
31
- repo.Transactions(
32
- WhereGivenConstructor[models.Account, models.Account],
33
- Find[models.Account, models.Account],
34
- )
35
- return *repo
36
- }
37
-
38
- func UpdateAccount(account models.Account) Repository[models.Account, models.Account] {
39
- repo := Construct[models.Account, models.Account](
40
- account,
41
- )
42
- repo.Transaction.Save(&repo.Constructor)
43
- repo.Result = repo.Constructor
44
- return *repo
45
- }
46
-
47
- func GetDetailAccountById(accountId uint) Repository[models.AccountDetails, models.AccountDetails] {
48
- repo := Construct[models.AccountDetails, models.AccountDetails](
49
- models.AccountDetails{AccountID: accountId},
50
- )
51
-
52
- // fmt.Println("Account ID:", repo.Constructor.AccountID)
53
- repo.Transactions(
54
- WhereGivenConstructor[models.AccountDetails, models.AccountDetails],
55
- Find[models.AccountDetails, models.AccountDetails],
56
- )
57
- return *repo
58
- }
59
-
60
- func CreateAccount(account models.Account) Repository[models.Account, models.Account] {
61
- repo := Construct[models.Account, models.Account](
62
- account,
63
- )
64
- Create(repo)
65
- return *repo
66
- }
67
-
68
- func CreateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
69
- repo := Construct[models.AccountDetails, models.AccountDetails](
70
- accountDetails,
71
- )
72
- Create(repo)
73
- return *repo
74
- }
75
-
76
- func UpdateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
77
- repo := Construct[models.AccountDetails, models.AccountDetails](
78
- models.AccountDetails{AccountID: accountDetails.AccountID},
79
- )
80
- repo.Transaction.Where("account_id = ?", accountDetails.AccountID).First(&repo.Constructor)
81
- accountDetails.ID = repo.Constructor.ID
82
- // fmt.Println(repo.Constructor)
83
- // fmt.Println(accountDetails)
84
- repo.Transaction.Updates(accountDetails)
85
- repo.Result = accountDetails
86
- return *repo
87
- }
 
1
+ package repositories
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ )
6
+
7
+ func GetAccountbyEmail(email string) Repository[models.Account, models.Account] {
8
+ repo := Construct[models.Account, models.Account](
9
+ models.Account{Email: email},
10
+ )
11
+ repo.Transactions(
12
+ WhereGivenConstructor[models.Account, models.Account],
13
+ Find[models.Account, models.Account],
14
+ )
15
+ return *repo
16
+ }
17
+
18
+ func GetAllAccount() Repository[models.Account, []models.Account] {
19
+ repo := Construct[models.Account, []models.Account](
20
+ models.Account{},
21
+ )
22
+ repo.Transactions(
23
+ Find[models.Account, []models.Account],
24
+ )
25
+ return *repo
26
+ }
27
+ func GetAccountById(accountId uint) Repository[models.Account, models.Account] {
28
+ repo := Construct[models.Account, models.Account](
29
+ models.Account{Id: accountId},
30
+ )
31
+ repo.Transactions(
32
+ WhereGivenConstructor[models.Account, models.Account],
33
+ Find[models.Account, models.Account],
34
+ )
35
+ return *repo
36
+ }
37
+
38
+ func UpdateAccount(account models.Account) Repository[models.Account, models.Account] {
39
+ repo := Construct[models.Account, models.Account](
40
+ account,
41
+ )
42
+ repo.Transaction.Save(&repo.Constructor)
43
+ repo.Result = repo.Constructor
44
+ return *repo
45
+ }
46
+
47
+ func GetDetailAccountById(accountId uint) Repository[models.AccountDetails, models.AccountDetails] {
48
+ repo := Construct[models.AccountDetails, models.AccountDetails](
49
+ models.AccountDetails{AccountID: accountId},
50
+ )
51
+
52
+ // fmt.Println("Account ID:", repo.Constructor.AccountID)
53
+ repo.Transactions(
54
+ WhereGivenConstructor[models.AccountDetails, models.AccountDetails],
55
+ Find[models.AccountDetails, models.AccountDetails],
56
+ )
57
+ return *repo
58
+ }
59
+
60
+ func CreateAccount(account models.Account) Repository[models.Account, models.Account] {
61
+ repo := Construct[models.Account, models.Account](
62
+ account,
63
+ )
64
+ Create(repo)
65
+ return *repo
66
+ }
67
+
68
+ func CreateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
69
+ repo := Construct[models.AccountDetails, models.AccountDetails](
70
+ accountDetails,
71
+ )
72
+ Create(repo)
73
+ return *repo
74
+ }
75
+
76
+ func UpdateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
77
+ repo := Construct[models.AccountDetails, models.AccountDetails](
78
+ models.AccountDetails{AccountID: accountDetails.AccountID},
79
+ )
80
+ repo.Transaction.Where("account_id = ?", accountDetails.AccountID).First(&repo.Constructor)
81
+ accountDetails.ID = repo.Constructor.ID
82
+ // fmt.Println(repo.Constructor)
83
+ // fmt.Println(accountDetails)
84
+ repo.Transaction.Updates(accountDetails)
85
+ repo.Result = accountDetails
86
+ return *repo
87
+ }
space/space/space/space/services/external_authentication_service.go CHANGED
@@ -1,81 +1,81 @@
1
- package services
2
-
3
- import (
4
- "context"
5
- "errors"
6
-
7
- "api.qobiltu.id/models"
8
- "api.qobiltu.id/repositories"
9
- uuid "github.com/satori/go.uuid"
10
- "google.golang.org/api/idtoken"
11
- )
12
-
13
- type GoogleAuthService struct {
14
- Service[models.ExternalAuth, models.AuthenticatedUser]
15
- }
16
-
17
- func (s *GoogleAuthService) Authenticate(isAgree bool) {
18
- GoogleAuth := repositories.GetExternalAccountByOauthId(s.Constructor.OauthID)
19
- payload, errGoogleAuth := idtoken.Validate(context.Background(), s.Constructor.OauthID, "")
20
- s.Error = errGoogleAuth
21
- if errGoogleAuth != nil {
22
- s.Exception.Unauthorized = true
23
- s.Exception.Message = "Oauth Provider Failed Login (Google Authentication)"
24
- return
25
- }
26
- email := payload.Claims["email"]
27
- checkRegisteredEmail := repositories.GetAccountbyEmail(email.(string))
28
- if !checkRegisteredEmail.NoRecord {
29
- token, _ := GenerateToken(&checkRegisteredEmail.Result)
30
- checkRegisteredEmail.Result.Password = "SECRET"
31
- s.Result = models.AuthenticatedUser{
32
- Account: checkRegisteredEmail.Result,
33
- Token: token,
34
- }
35
- return
36
- }
37
- if GoogleAuth.NoRecord {
38
- if !isAgree {
39
- s.Exception.BadRequest = true
40
- s.Exception.Message = "Please agree to the terms and conditions to create an account"
41
- return
42
- }
43
- s.Constructor.UUID = uuid.NewV4()
44
- s.Constructor.OauthProvider = "Google"
45
-
46
- createAccount := repositories.CreateAccount(models.Account{
47
- UUID: uuid.NewV4(),
48
- Email: email.(string),
49
- IsEmailVerified: true,
50
- })
51
-
52
- s.Constructor.AccountID = createAccount.Result.Id
53
- createGoogleAuth := repositories.CreateExternalAuth(s.Constructor)
54
-
55
- GoogleAuth.Result.AccountID = createGoogleAuth.Result.AccountID
56
- userProfile := UserProfileService{}
57
- userProfile.Constructor.AccountID = GoogleAuth.Result.AccountID
58
- userProfile.Create()
59
- if userProfile.Error != nil {
60
- s.Error = userProfile.Error
61
- return
62
- }
63
- s.Error = createGoogleAuth.RowsError
64
- s.Error = errors.Join(s.Error, createAccount.RowsError)
65
- }
66
-
67
- accountData := repositories.GetAccountById(GoogleAuth.Result.AccountID)
68
- token, err_tok := GenerateToken(&accountData.Result)
69
-
70
- if err_tok != nil {
71
- s.Error = errors.Join(s.Error, err_tok)
72
- }
73
-
74
- accountData.Result.Password = "SECRET"
75
- s.Result = models.AuthenticatedUser{
76
- Account: accountData.Result,
77
- Token: token,
78
- }
79
- s.Error = accountData.RowsError
80
-
81
- }
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/repositories"
9
+ uuid "github.com/satori/go.uuid"
10
+ "google.golang.org/api/idtoken"
11
+ )
12
+
13
+ type GoogleAuthService struct {
14
+ Service[models.ExternalAuth, models.AuthenticatedUser]
15
+ }
16
+
17
+ func (s *GoogleAuthService) Authenticate(isAgree bool) {
18
+ GoogleAuth := repositories.GetExternalAccountByOauthId(s.Constructor.OauthID)
19
+ payload, errGoogleAuth := idtoken.Validate(context.Background(), s.Constructor.OauthID, "")
20
+ s.Error = errGoogleAuth
21
+ if errGoogleAuth != nil {
22
+ s.Exception.Unauthorized = true
23
+ s.Exception.Message = "Oauth Provider Failed Login (Google Authentication)"
24
+ return
25
+ }
26
+ email := payload.Claims["email"]
27
+ checkRegisteredEmail := repositories.GetAccountbyEmail(email.(string))
28
+ if !checkRegisteredEmail.NoRecord {
29
+ token, _ := GenerateToken(&checkRegisteredEmail.Result)
30
+ checkRegisteredEmail.Result.Password = "SECRET"
31
+ s.Result = models.AuthenticatedUser{
32
+ Account: checkRegisteredEmail.Result,
33
+ Token: token,
34
+ }
35
+ return
36
+ }
37
+ if GoogleAuth.NoRecord {
38
+ if !isAgree {
39
+ s.Exception.BadRequest = true
40
+ s.Exception.Message = "Please agree to the terms and conditions to create an account"
41
+ return
42
+ }
43
+ s.Constructor.UUID = uuid.NewV4()
44
+ s.Constructor.OauthProvider = "Google"
45
+
46
+ createAccount := repositories.CreateAccount(models.Account{
47
+ UUID: uuid.NewV4(),
48
+ Email: email.(string),
49
+ IsEmailVerified: true,
50
+ })
51
+
52
+ s.Constructor.AccountID = createAccount.Result.Id
53
+ createGoogleAuth := repositories.CreateExternalAuth(s.Constructor)
54
+
55
+ GoogleAuth.Result.AccountID = createGoogleAuth.Result.AccountID
56
+ userProfile := UserProfileService{}
57
+ userProfile.Constructor.AccountID = GoogleAuth.Result.AccountID
58
+ userProfile.Create()
59
+ if userProfile.Error != nil {
60
+ s.Error = userProfile.Error
61
+ return
62
+ }
63
+ s.Error = createGoogleAuth.RowsError
64
+ s.Error = errors.Join(s.Error, createAccount.RowsError)
65
+ }
66
+
67
+ accountData := repositories.GetAccountById(GoogleAuth.Result.AccountID)
68
+ token, err_tok := GenerateToken(&accountData.Result)
69
+
70
+ if err_tok != nil {
71
+ s.Error = errors.Join(s.Error, err_tok)
72
+ }
73
+
74
+ accountData.Result.Password = "SECRET"
75
+ s.Result = models.AuthenticatedUser{
76
+ Account: accountData.Result,
77
+ Token: token,
78
+ }
79
+ s.Error = accountData.RowsError
80
+
81
+ }
space/space/space/space/services/user_profile_service.go CHANGED
@@ -53,12 +53,12 @@ func (s *UserProfileService) Create() {
53
  return
54
  }
55
  s.Result = models.UserProfileResponse{
56
- Account: repositories.GetAccountById(s.Constructor.AccountID).Result,
57
  Details: userProfile.Result,
58
  }
59
  }
60
  func (s *UserProfileService) Retrieve() {
61
- userProfile := repositories.GetDetailAccountById(s.Constructor.AccountID)
62
  s.Error = userProfile.RowsError
63
  if userProfile.NoRecord {
64
  s.Exception.DataNotFound = true
@@ -66,7 +66,7 @@ func (s *UserProfileService) Retrieve() {
66
  return
67
  }
68
  s.Result = models.UserProfileResponse{
69
- Account: repositories.GetAccountById(s.Constructor.AccountID).Result,
70
  Details: userProfile.Result,
71
  }
72
  s.Result.Account.Password = "SECRET"
@@ -96,7 +96,7 @@ func (s *UserProfileService) Update() {
96
  s.Exception.Message = "There is no account with given credentials!"
97
  return
98
  }
99
- account := repositories.GetAccountById(s.Constructor.AccountID)
100
  account.Result.IsDetailCompleted = (userProfile.Result.InitialName != "" &&
101
  userProfile.Result.FullName != nil &&
102
  userProfile.Result.DateOfBirth != nil &&
 
53
  return
54
  }
55
  s.Result = models.UserProfileResponse{
56
+ Account: repositories.GetAccountById(uint(s.Constructor.AccountID)).Result,
57
  Details: userProfile.Result,
58
  }
59
  }
60
  func (s *UserProfileService) Retrieve() {
61
+ userProfile := repositories.GetDetailAccountById(uint(s.Constructor.AccountID))
62
  s.Error = userProfile.RowsError
63
  if userProfile.NoRecord {
64
  s.Exception.DataNotFound = true
 
66
  return
67
  }
68
  s.Result = models.UserProfileResponse{
69
+ Account: repositories.GetAccountById(uint(s.Constructor.AccountID)).Result,
70
  Details: userProfile.Result,
71
  }
72
  s.Result.Account.Password = "SECRET"
 
96
  s.Exception.Message = "There is no account with given credentials!"
97
  return
98
  }
99
+ account := repositories.GetAccountById(uint(s.Constructor.AccountID))
100
  account.Result.IsDetailCompleted = (userProfile.Result.InitialName != "" &&
101
  userProfile.Result.FullName != nil &&
102
  userProfile.Result.DateOfBirth != nil &&
space/space/space/space/space/config/database_connection_config.go CHANGED
@@ -70,6 +70,10 @@ func AutoMigrateAll(db *gorm.DB) {
70
  &models.Question{},
71
  &models.Answer{},
72
  &models.UserAnswer{},
 
 
 
 
73
  )
74
 
75
  if err != nil {
 
70
  &models.Question{},
71
  &models.Answer{},
72
  &models.UserAnswer{},
73
+ &models.PersonalityAndPreferenceCV{},
74
+ &models.FamilyMemberCV{},
75
+ &models.PhysicalAndHealthCV{},
76
+ &models.WorshipAndReligiousUnderstandingCV{},
77
  )
78
 
79
  if err != nil {
space/space/space/space/space/controller/cv/cv_controller.go ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cv_controller
2
+
3
+ import (
4
+ "api.qobiltu.id/middleware"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/response"
7
+ "api.qobiltu.id/services"
8
+ "github.com/gin-gonic/gin"
9
+ "net/http"
10
+ "strconv"
11
+ )
12
+
13
+ type CVController interface {
14
+ SaveAccountDetails(ctx *gin.Context)
15
+ GetAccountDetails(ctx *gin.Context)
16
+
17
+ SavePersonalityAndPreference(ctx *gin.Context)
18
+ GetPersonalityAndPreference(ctx *gin.Context)
19
+
20
+ CreateFamilyMember(ctx *gin.Context)
21
+ UpdateFamilyMember(ctx *gin.Context)
22
+ ListFamilyMember(ctx *gin.Context)
23
+ GetFamilyMember(ctx *gin.Context)
24
+ DeleteFamilyMember(ctx *gin.Context)
25
+
26
+ SavePhysicalAndHealth(ctx *gin.Context)
27
+ GetPhysicalAndHealth(ctx *gin.Context)
28
+
29
+ SaveWorshipAndReligiousUnderstanding(ctx *gin.Context)
30
+ GetWorshipAndReligiousUnderstanding(ctx *gin.Context)
31
+ }
32
+
33
+ type cvController struct {
34
+ cvService services.CVService
35
+ }
36
+
37
+ func NewCVController(cvService services.CVService) CVController {
38
+ return &cvController{
39
+ cvService: cvService,
40
+ }
41
+ }
42
+
43
+ // --- Account Details ---
44
+ func (c *cvController) SaveAccountDetails(ctx *gin.Context) {
45
+ var req models.AccountDetailsRequest
46
+ if err := ctx.ShouldBindJSON(&req); err != nil {
47
+ response.HandleError(ctx, err)
48
+ return
49
+ }
50
+
51
+ accountData := middleware.GetAccountData(ctx)
52
+ req.AccountID = int64(accountData.UserID)
53
+
54
+ res, err := c.cvService.SaveAccountDetails(ctx, &req)
55
+ if err != nil {
56
+ response.HandleError(ctx, err)
57
+ return
58
+ }
59
+
60
+ response.HandleSuccess(ctx, http.StatusOK, "Account details saved", res, nil)
61
+ }
62
+
63
+ func (c *cvController) GetAccountDetails(ctx *gin.Context) {
64
+ accountData := middleware.GetAccountData(ctx)
65
+ accountID := int64(accountData.UserID)
66
+
67
+ res, err := c.cvService.GetAccountDetails(ctx, accountID)
68
+ if err != nil {
69
+ response.HandleError(ctx, err)
70
+ return
71
+ }
72
+
73
+ response.HandleSuccess(ctx, http.StatusOK, "Get account details success", res, nil)
74
+ }
75
+
76
+ // --- Personality & Preference ---
77
+
78
+ func (c *cvController) SavePersonalityAndPreference(ctx *gin.Context) {
79
+ var req models.PersonalityAndPreferenceCVRequest
80
+ if err := ctx.ShouldBindJSON(&req); err != nil {
81
+ response.HandleError(ctx, err)
82
+ return
83
+ }
84
+
85
+ accountData := middleware.GetAccountData(ctx)
86
+ req.AccountID = int64(accountData.UserID)
87
+
88
+ res, err := c.cvService.SavePersonalityAndPreference(ctx, &req)
89
+ if err != nil {
90
+ response.HandleError(ctx, err)
91
+ return
92
+ }
93
+
94
+ response.HandleSuccess(ctx, http.StatusOK, "Personality and Preference saved", res, nil)
95
+ }
96
+
97
+ func (c *cvController) GetPersonalityAndPreference(ctx *gin.Context) {
98
+ accountData := middleware.GetAccountData(ctx)
99
+ accountID := int64(accountData.UserID)
100
+
101
+ res, err := c.cvService.GetPersonalityAndPreference(ctx, accountID)
102
+ if err != nil {
103
+ response.HandleError(ctx, err)
104
+ return
105
+ }
106
+
107
+ response.HandleSuccess(ctx, http.StatusOK, "Get Personality and Preference success", res, nil)
108
+ }
109
+
110
+ // --- Family Member ---
111
+
112
+ func (c *cvController) CreateFamilyMember(ctx *gin.Context) {
113
+ var req models.FamilyMemberRequest
114
+ if err := ctx.ShouldBindJSON(&req); err != nil {
115
+ response.HandleError(ctx, err)
116
+ return
117
+ }
118
+
119
+ accountData := middleware.GetAccountData(ctx)
120
+ req.AccountID = int64(accountData.UserID)
121
+
122
+ res, err := c.cvService.CreateFamilyMember(ctx, &req)
123
+ if err != nil {
124
+ response.HandleError(ctx, err)
125
+ return
126
+ }
127
+
128
+ response.HandleSuccess(ctx, http.StatusOK, "Family member saved", res, nil)
129
+ }
130
+
131
+ func (c *cvController) UpdateFamilyMember(ctx *gin.Context) {
132
+ idStr := ctx.Param("id")
133
+ id, err := strconv.ParseInt(idStr, 10, 64)
134
+ if err != nil {
135
+ response.HandleError(ctx, err)
136
+ return
137
+ }
138
+
139
+ var req models.FamilyMemberRequest
140
+ if err := ctx.ShouldBindJSON(&req); err != nil {
141
+ response.HandleError(ctx, err)
142
+ return
143
+ }
144
+
145
+ res, err := c.cvService.UpdateFamilyMember(ctx, id, &req)
146
+ if err != nil {
147
+ response.HandleError(ctx, err)
148
+ return
149
+ }
150
+
151
+ response.HandleSuccess(ctx, http.StatusOK, "Family member updated", res, nil)
152
+ }
153
+
154
+ func (c *cvController) ListFamilyMember(ctx *gin.Context) {
155
+ accountData := middleware.GetAccountData(ctx)
156
+ accountID := int64(accountData.UserID)
157
+
158
+ list, err := c.cvService.ListFamilyMember(ctx, accountID)
159
+ if err != nil {
160
+ response.HandleError(ctx, err)
161
+ return
162
+ }
163
+
164
+ response.HandleSuccess(ctx, http.StatusOK, "List family members", list, nil)
165
+ }
166
+
167
+ func (c *cvController) GetFamilyMember(ctx *gin.Context) {
168
+ idStr := ctx.Param("id")
169
+ id, err := strconv.ParseInt(idStr, 10, 64)
170
+ if err != nil {
171
+ response.HandleError(ctx, err)
172
+ return
173
+ }
174
+
175
+ res, err := c.cvService.GetFamilyMember(ctx, id)
176
+ if err != nil {
177
+ response.HandleError(ctx, err)
178
+ return
179
+ }
180
+
181
+ response.HandleSuccess(ctx, http.StatusOK, "Get family member success", res, nil)
182
+ }
183
+
184
+ func (c *cvController) DeleteFamilyMember(ctx *gin.Context) {
185
+ idStr := ctx.Param("id")
186
+ id, err := strconv.ParseInt(idStr, 10, 64)
187
+ if err != nil {
188
+ response.HandleError(ctx, err)
189
+ return
190
+ }
191
+
192
+ err = c.cvService.DeleteFamilyMember(ctx, id)
193
+ if err != nil {
194
+ response.HandleError(ctx, err)
195
+ return
196
+ }
197
+
198
+ response.HandleSuccess(ctx, http.StatusOK, "Family member deleted", nil, nil)
199
+ }
200
+
201
+ // --- Physical and Health ---
202
+
203
+ func (c *cvController) SavePhysicalAndHealth(ctx *gin.Context) {
204
+ var req models.PhysicalAndHealthRequest
205
+ if err := ctx.ShouldBindJSON(&req); err != nil {
206
+ response.HandleError(ctx, err)
207
+ return
208
+ }
209
+
210
+ accountData := middleware.GetAccountData(ctx)
211
+ req.AccountID = int64(accountData.UserID)
212
+
213
+ res, err := c.cvService.SavePhysicalAndHealth(ctx, &req)
214
+ if err != nil {
215
+ response.HandleError(ctx, err)
216
+ return
217
+ }
218
+
219
+ response.HandleSuccess(ctx, http.StatusOK, "Physical and health saved", res, nil)
220
+ }
221
+
222
+ func (c *cvController) GetPhysicalAndHealth(ctx *gin.Context) {
223
+ accountData := middleware.GetAccountData(ctx)
224
+ accountID := int64(accountData.UserID)
225
+
226
+ res, err := c.cvService.GetPhysicalAndHealth(ctx, accountID)
227
+ if err != nil {
228
+ response.HandleError(ctx, err)
229
+ return
230
+ }
231
+
232
+ response.HandleSuccess(ctx, http.StatusOK, "Get physical and health success", res, nil)
233
+ }
234
+
235
+ // --- Worship and Religious Understanding ---
236
+
237
+ func (c *cvController) SaveWorshipAndReligiousUnderstanding(ctx *gin.Context) {
238
+ var req models.WorshipAndReligiousUnderstandingRequest
239
+ if err := ctx.ShouldBindJSON(&req); err != nil {
240
+ response.HandleError(ctx, err)
241
+ return
242
+ }
243
+
244
+ accountData := middleware.GetAccountData(ctx)
245
+ req.AccountID = int64(accountData.UserID)
246
+
247
+ res, err := c.cvService.SaveWorshipAndReligiousUnderstanding(ctx, &req)
248
+ if err != nil {
249
+ response.HandleError(ctx, err)
250
+ return
251
+ }
252
+
253
+ response.HandleSuccess(ctx, http.StatusOK, "Worship and religious understanding saved", res, nil)
254
+ }
255
+
256
+ func (c *cvController) GetWorshipAndReligiousUnderstanding(ctx *gin.Context) {
257
+ accountData := middleware.GetAccountData(ctx)
258
+ accountID := int64(accountData.UserID)
259
+
260
+ res, err := c.cvService.GetWorshipAndReligiousUnderstanding(ctx, accountID)
261
+ if err != nil {
262
+ response.HandleError(ctx, err)
263
+ return
264
+ }
265
+
266
+ response.HandleSuccess(ctx, http.StatusOK, "Get worship and religious understanding success", res, nil)
267
+ }
space/space/space/space/space/main.go CHANGED
@@ -2,6 +2,7 @@ package main
2
 
3
  import (
4
  "api.qobiltu.id/config"
 
5
  "api.qobiltu.id/controller/health_check"
6
  "api.qobiltu.id/mail"
7
  "api.qobiltu.id/repositories"
@@ -46,13 +47,20 @@ func main() {
46
  healthCheckService := services.NewHealthCheckService(healthCheckRepository)
47
  healthCheckController := health_check_controller.NewHealthCheckController(healthCheckService)
48
 
 
 
 
 
49
  // start task processor
50
  err = taskProcessor.Start()
51
  utils.FatalIfErr("failed to start task processor", err)
52
  slog.Info("Task processor started")
53
 
54
  // create server
55
- s, err := router.NewServer(healthCheckController)
 
 
 
56
  utils.FatalIfErr("failed to create server", err)
57
 
58
  // run server
 
2
 
3
  import (
4
  "api.qobiltu.id/config"
5
+ cv_controller "api.qobiltu.id/controller/cv"
6
  "api.qobiltu.id/controller/health_check"
7
  "api.qobiltu.id/mail"
8
  "api.qobiltu.id/repositories"
 
47
  healthCheckService := services.NewHealthCheckService(healthCheckRepository)
48
  healthCheckController := health_check_controller.NewHealthCheckController(healthCheckService)
49
 
50
+ cvRepository := repositories.NewCVRepository(config.DB)
51
+ cvService := services.NewCVService(cvRepository)
52
+ cvController := cv_controller.NewCVController(cvService)
53
+
54
  // start task processor
55
  err = taskProcessor.Start()
56
  utils.FatalIfErr("failed to start task processor", err)
57
  slog.Info("Task processor started")
58
 
59
  // create server
60
+ s, err := router.NewServer(
61
+ healthCheckController,
62
+ cvController,
63
+ )
64
  utils.FatalIfErr("failed to create server", err)
65
 
66
  // run server
space/space/space/space/space/middleware/authentication_middleware.go CHANGED
@@ -33,5 +33,12 @@ func AuthUser(c *gin.Context) {
33
  c.Abort()
34
  return
35
  }
 
36
 
 
 
 
 
 
 
37
  }
 
33
  c.Abort()
34
  return
35
  }
36
+ }
37
 
38
+ func GetAccountData(c *gin.Context) models.AccountData {
39
+ cParam, _ := c.Get("accountData")
40
+ if cParam != nil {
41
+ return cParam.(models.AccountData)
42
+ }
43
+ return models.AccountData{}
44
  }
space/space/space/space/space/models/database_orm_model.go CHANGED
@@ -18,19 +18,21 @@ type Account struct {
18
  }
19
 
20
  type AccountDetails struct {
21
- ID uint64 `gorm:"primaryKey" json:"id"`
22
- AccountID uint `json:"account_id"`
23
- InitialName string `json:"initial_name"`
24
- FullName *string `json:"full_name"`
25
- DateOfBirth *time.Time `json:"date_of_birth"`
26
- PlaceOfBirth *string `json:"place_of_birth"`
27
- Domicile *string `json:"domicile"`
28
- LastJob *string `json:"last_job"`
29
- Gender *string `json:"gender"`
30
- LastEducation *string `json:"last_education"`
31
- MaritalStatus *string `json:"marital_status"`
32
- Avatar *string `json:"avatar"`
33
- PhoneNumber *string `json:"phone_number"`
 
 
34
  }
35
 
36
  type EmailVerification struct {
@@ -184,24 +186,107 @@ type QuizResult struct {
184
  AverageScore float64 `gorm:"column:average_score" json:"average_score"`
185
  }
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  // Gorm table name settings
188
- func (Account) TableName() string { return "account" }
189
- func (AccountDetails) TableName() string { return "account_details" }
190
- func (EmailVerification) TableName() string { return "email_verifications" }
191
- func (ExternalAuth) TableName() string { return "extern_auth" }
192
- func (FCM) TableName() string { return "fcm" }
193
- func (ForgotPassword) TableName() string { return "forgot_password" }
194
- func (Academy) TableName() string { return "academy" }
195
- func (AcademyMaterial) TableName() string { return "academy_materials" }
196
- func (AcademyContent) TableName() string { return "academy_contents" }
197
- func (AcademyMaterialProgress) TableName() string { return "academy_materials_progress" }
198
- func (AcademyContentProgress) TableName() string { return "academy_contents_progress" }
199
- func (RegionProvince) TableName() string { return "region_provinces" }
200
- func (RegionCity) TableName() string { return "region_cities" }
201
- func (Answer) TableName() string { return "answers" }
202
- func (Question) TableName() string { return "questions" }
203
- func (Quiz) TableName() string { return "quizzes" }
204
- func (QuizAttempt) TableName() string { return "quiz_attempts" }
205
- func (UserAnswer) TableName() string { return "user_answers" }
206
- func (OptionCategory) TableName() string { return "option_categories" }
207
- func (OptionValues) TableName() string { return "option_values" }
 
 
 
 
 
 
 
18
  }
19
 
20
  type AccountDetails struct {
21
+ ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
22
+ AccountID uint `gorm:"column:account_id;not null;unique" json:"account_id"`
23
+ InitialName string `gorm:"column:initial_name;not null" json:"initial_name"`
24
+ FullName *string `gorm:"column:full_name" json:"full_name"`
25
+ DateOfBirth *time.Time `gorm:"column:date_of_birth" json:"date_of_birth"`
26
+ PlaceOfBirth *string `gorm:"column:place_of_birth" json:"place_of_birth"`
27
+ Domicile *string `gorm:"column:domicile" json:"domicile"`
28
+ LastJob *string `gorm:"column:last_job" json:"last_job"`
29
+ Gender *string `gorm:"column:gender" json:"gender"`
30
+ LastEducation *string `gorm:"column:last_education" json:"last_education"`
31
+ MaritalStatus *string `gorm:"column:marital_status" json:"marital_status"`
32
+ Avatar *string `gorm:"column:avatar" json:"avatar"`
33
+ PhoneNumber *string `gorm:"column:phone_number" json:"phone_number"`
34
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
35
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
36
  }
37
 
38
  type EmailVerification struct {
 
186
  AverageScore float64 `gorm:"column:average_score" json:"average_score"`
187
  }
188
 
189
+ type (
190
+ PersonalityAndPreferenceCV struct {
191
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
192
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
193
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
194
+ PositiveTraits *string `gorm:"column:positive_traits" json:"positive_traits"` // sifat positif
195
+ NegativeTraits *string `gorm:"column:negative_traits" json:"negative_traits"` // sifat negatif
196
+ Hobbies *string `gorm:"column:hobbies" json:"hobbies"` // hobi
197
+ LifeGoals *string `gorm:"column:life_goals" json:"life_goals"` // target hidup
198
+ DailyActivities *string `gorm:"column:daily_activities" json:"daily_activities"` // kegiatan sehari-hari
199
+ LeisureActivities *string `gorm:"column:leisure_activities" json:"leisure_activities"` // kegiatan waktu luang
200
+ Likes *string `gorm:"column:likes" json:"likes"` // hal yang disukai
201
+ Dislikes *string `gorm:"column:dislikes" json:"dislikes"` // hal yang tidak disukai
202
+ StressHandling *string `gorm:"column:stress_handling" json:"stress_handling"` // cara mengatasi stres
203
+ AngerTriggers *string `gorm:"column:anger_triggers" json:"anger_triggers"` // pemicu amarah
204
+ FavoriteFoodAndDrinks *string `gorm:"column:favorite_food_and_drinks" json:"favorite_food_and_drinks"` // makanan dan minuman favorit
205
+ CanCook *bool `gorm:"column:can_cook" json:"can_cook"` // bisa memasak
206
+ TypesOfDishesCooked *string `gorm:"column:types_of_dishes_cooked" json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
207
+ MonthlyExpenses *string `gorm:"column:monthly_expenses" json:"monthly_expenses"` // pengeluaran per bulan
208
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
209
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
210
+ }
211
+
212
+ FamilyMemberCV struct {
213
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
214
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id"`
215
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
216
+ Role *string `gorm:"column:role" json:"role"` // Peran dalam keluarga
217
+ Status *string `gorm:"column:status" json:"status"` // Status (Hidup, Wafat)
218
+ Religion *string `gorm:"column:religion" json:"religion"` // Agama
219
+ Job *string `gorm:"column:job" json:"job"` // Pekerjaan
220
+ LastEducation *string `gorm:"column:last_education" json:"last_education"` // Pendidikan terakhir
221
+ Age *int `gorm:"column:age" json:"age"` // Usia
222
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
223
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
224
+ }
225
+
226
+ PhysicalAndHealthCV struct {
227
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
228
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
229
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
230
+ HeightInCm *int `gorm:"column:height_cm" json:"height_cm"` // Tinggi badan dalam satuan sentimeter
231
+ WeightInKg *int `gorm:"column:weight_kg" json:"weight_kg"` // Berat badan dalam satuan kilogram
232
+ BodyShape *string `gorm:"column:body_shape" json:"body_shape"` // Bentuk tubuh
233
+ SkinColor *string `gorm:"column:skin_color" json:"skin_color"` // Warna kulit
234
+ HairType *string `gorm:"column:hair_type" json:"hair_type"` // Tipe rambut
235
+ MedicalHistory *string `gorm:"column:medical_history" json:"medical_history"` // Riwayat penyakit
236
+ PhysicalDisorder *string `gorm:"column:physical_disorder" json:"physical_disorder"` // Cacat fisik
237
+ PhysicalTraits *string `gorm:"column:physical_traits" json:"physical_traits"` // Ciri khas fisik
238
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // Waktu data dibuat
239
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // Waktu data terakhir diperbarui
240
+ }
241
+
242
+ WorshipAndReligiousUnderstandingCV struct {
243
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
244
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
245
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
246
+ ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
247
+ CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
248
+ TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
249
+ DhuhaPrayer *string `gorm:"column:dhuha_prayer" json:"dhuha_prayer"` // sholat_dhuha
250
+ QuranMemorization *string `gorm:"column:quran_memorization" json:"quran_memorization"` // hafalan_alquran
251
+ QuranReadingAbility *string `gorm:"column:quran_reading_ability" json:"quran_reading_ability"` // kemampuan_baca_alquran
252
+ DaudFasting *string `gorm:"column:daud_fasting" json:"daud_fasting"` // puasa_daud
253
+ AyyamulBidhFasting *string `gorm:"column:ayyamul_bidh_fasting" json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
254
+ HajjOrUmrah *string `gorm:"column:hajj_or_umrah" json:"hajj_or_umrah"` // ibadah_haji_umroh
255
+ ListeningToMusic *string `gorm:"column:listening_to_music" json:"listening_to_music"` // mendengarkan_musik
256
+ OpinionOnIkhtilat *string `gorm:"column:opinion_on_ikhtilat" json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
257
+ OpinionOnTouchingNonMahram *string `gorm:"column:opinion_on_touching_non_mahram" json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
258
+ OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
259
+ WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
260
+ FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
261
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
262
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
263
+ }
264
+ )
265
+
266
  // Gorm table name settings
267
+ func (Account) TableName() string { return "account" }
268
+ func (AccountDetails) TableName() string { return "account_details" }
269
+ func (EmailVerification) TableName() string { return "email_verifications" }
270
+ func (ExternalAuth) TableName() string { return "extern_auth" }
271
+ func (FCM) TableName() string { return "fcm" }
272
+ func (ForgotPassword) TableName() string { return "forgot_password" }
273
+ func (Academy) TableName() string { return "academy" }
274
+ func (AcademyMaterial) TableName() string { return "academy_materials" }
275
+ func (AcademyContent) TableName() string { return "academy_contents" }
276
+ func (AcademyMaterialProgress) TableName() string { return "academy_materials_progress" }
277
+ func (AcademyContentProgress) TableName() string { return "academy_contents_progress" }
278
+ func (RegionProvince) TableName() string { return "region_provinces" }
279
+ func (RegionCity) TableName() string { return "region_cities" }
280
+ func (Answer) TableName() string { return "answers" }
281
+ func (Question) TableName() string { return "questions" }
282
+ func (Quiz) TableName() string { return "quizzes" }
283
+ func (QuizAttempt) TableName() string { return "quiz_attempts" }
284
+ func (UserAnswer) TableName() string { return "user_answers" }
285
+ func (OptionCategory) TableName() string { return "option_categories" }
286
+ func (OptionValues) TableName() string { return "option_values" }
287
+ func (PersonalityAndPreferenceCV) TableName() string { return "personality_and_preference_cv" }
288
+ func (FamilyMemberCV) TableName() string { return "family_member_cv" }
289
+ func (PhysicalAndHealthCV) TableName() string { return "physical_and_health_cv" }
290
+ func (WorshipAndReligiousUnderstandingCV) TableName() string {
291
+ return "worship_and_religious_understanding_cv"
292
+ }
space/space/space/space/space/models/request_model.go CHANGED
@@ -1,51 +1,126 @@
1
- package models
2
-
3
- type LoginRequest struct {
4
- Email string `json:"email" binding:"required"`
5
- Password string `json:"password" binding:"required"`
6
- }
7
-
8
- type RegisterRequest struct {
9
- Name string `json:"name"`
10
- Email string `json:"email" binding:"required,email"`
11
- Phone int `json:"phone"`
12
- Password string `json:"password" binding:"required"`
13
- }
14
-
15
- type ChangePasswordRequest struct {
16
- OldPassword string `json:"old_password" binding:"required" `
17
- NewPassword string `json:"new_password" binding:"required" `
18
- }
19
-
20
- type CreateVerifyEmailRequest struct {
21
- Token uint `json:"token" binding:"required"`
22
- }
23
-
24
- type OptionsRequest struct {
25
- OptionName string `json:"option_name" binding:"required"`
26
- OptionValue []string `json:"option_values" binding:"required"`
27
- }
28
-
29
- type ExternalAuthRequest struct {
30
- OauthID string `json:"oauth_id" binding:"required"`
31
- OauthProvider string `json:"oauth_provider" binding:"required"`
32
- IsAgreeTerms bool `json:"is_agree_terms"`
33
- IsSexualDisease bool `json:"is_sexual_disease"`
34
- }
35
-
36
- type ForgotPasswordRequest struct {
37
- Email string `json:"email" binding:"required,email"`
38
- }
39
- type ValidateForgotPasswordRequest struct {
40
- Token uint `json:"token" binding:"required"`
41
- NewPassword string `json:"new_password"`
42
- }
43
-
44
- type QuestionQuizRequest struct {
45
- QuestionNo int `json:"question_no" binding:"required"`
46
- }
47
-
48
- type AnswerQuizRequest struct {
49
- QuestionNo int `json:"question_no" binding:"required"`
50
- Answer int `json:"answer" binding:"required"`
51
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package models
2
+
3
+ import "time"
4
+
5
+ type LoginRequest struct {
6
+ Email string `json:"email" binding:"required"`
7
+ Password string `json:"password" binding:"required"`
8
+ }
9
+
10
+ type RegisterRequest struct {
11
+ Name string `json:"name"`
12
+ Email string `json:"email" binding:"required,email"`
13
+ Phone int `json:"phone"`
14
+ Password string `json:"password" binding:"required"`
15
+ }
16
+
17
+ type ChangePasswordRequest struct {
18
+ OldPassword string `json:"old_password" binding:"required" `
19
+ NewPassword string `json:"new_password" binding:"required" `
20
+ }
21
+
22
+ type CreateVerifyEmailRequest struct {
23
+ Token uint `json:"token" binding:"required"`
24
+ }
25
+
26
+ type OptionsRequest struct {
27
+ OptionName string `json:"option_name" binding:"required"`
28
+ OptionValue []string `json:"option_values" binding:"required"`
29
+ }
30
+
31
+ type ExternalAuthRequest struct {
32
+ OauthID string `json:"oauth_id" binding:"required"`
33
+ OauthProvider string `json:"oauth_provider" binding:"required"`
34
+ IsAgreeTerms bool `json:"is_agree_terms"`
35
+ IsSexualDisease bool `json:"is_sexual_disease"`
36
+ }
37
+
38
+ type ForgotPasswordRequest struct {
39
+ Email string `json:"email" binding:"required,email"`
40
+ }
41
+ type ValidateForgotPasswordRequest struct {
42
+ Token uint `json:"token" binding:"required"`
43
+ NewPassword string `json:"new_password"`
44
+ }
45
+
46
+ type QuestionQuizRequest struct {
47
+ QuestionNo int `json:"question_no" binding:"required"`
48
+ }
49
+
50
+ type AnswerQuizRequest struct {
51
+ QuestionNo int `json:"question_no" binding:"required"`
52
+ Answer int `json:"answer" binding:"required"`
53
+ }
54
+
55
+ type (
56
+ PersonalityAndPreferenceCVRequest struct {
57
+ AccountID int64 `json:"-"`
58
+ PositiveTraits *string `json:"positive_traits"` // sifat positif
59
+ NegativeTraits *string `json:"negative_traits"` // sifat negatif
60
+ Hobbies *string `json:"hobbies"` // hobi
61
+ LifeGoals *string `json:"life_goals"` // target hidup
62
+ DailyActivities *string `json:"daily_activities"` // kegiatan sehari-hari
63
+ LeisureActivities *string `json:"leisure_activities"` // kegiatan waktu luang
64
+ Likes *string `json:"likes"` // hal yang disukai
65
+ Dislikes *string `json:"dislikes"` // hal yang tidak disukai
66
+ StressHandling *string `json:"stress_handling"` // cara mengatasi stres
67
+ AngerTriggers *string `json:"anger_triggers"` // pemicu amarah
68
+ FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
69
+ CanCook *bool `json:"can_cook"` // bisa memasak
70
+ TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
71
+ MonthlyExpenses *string `json:"monthly_expenses"` // pengeluaran per bulan
72
+ }
73
+
74
+ FamilyMemberRequest struct {
75
+ AccountID int64 `json:"-"`
76
+ Role *string `json:"role"` // Peran dalam keluarga
77
+ Status *string `json:"status"` // Status (Hidup, Wafat)
78
+ Religion *string `json:"religion"` // Agama
79
+ Job *string `json:"job"` // Pekerjaan
80
+ LastEducation *string `json:"last_education"` // Pendidikan terakhir
81
+ Age *int `json:"age"` // Usia
82
+ }
83
+
84
+ PhysicalAndHealthRequest struct {
85
+ AccountID int64 `json:"-"`
86
+ HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
87
+ WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
88
+ BodyShape *string `json:"body_shape"` // Bentuk tubuh
89
+ SkinColor *string `json:"skin_color"` // Warna kulit
90
+ HairType *string `json:"hair_type"` // Tipe rambut
91
+ MedicalHistory *string `json:"medical_history"` // Riwayat penyakit
92
+ PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
93
+ PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
94
+ }
95
+
96
+ AccountDetailsRequest struct {
97
+ AccountID int64 `json:"-"`
98
+ FullName *string `json:"full_name"`
99
+ Gender *string `json:"gender"`
100
+ DateOfBirth *time.Time `json:"date_of_birth"`
101
+ PlaceOfBirth *string `json:"place_of_birth"`
102
+ Domicile *string `json:"domicile"`
103
+ MaritalStatus *string `json:"marital_status"`
104
+ LastEducation *string `json:"last_education"`
105
+ LastJob *string `json:"last_job"`
106
+ }
107
+
108
+ WorshipAndReligiousUnderstandingRequest struct {
109
+ AccountID int64 `json:"-"`
110
+ ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
111
+ CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
112
+ TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
113
+ DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
114
+ QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
115
+ QuranReadingAbility *string `json:"quran_reading_ability"` // kemampuan_baca_alquran
116
+ DaudFasting *string `json:"daud_fasting"` // puasa_daud
117
+ AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
118
+ HajjOrUmrah *string `json:"hajj_or_umrah"` // ibadah_haji_umroh
119
+ ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
120
+ OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
121
+ OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
122
+ OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
123
+ WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
124
+ FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
125
+ }
126
+ )
space/space/space/space/space/repositories/cv_repository.go ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ "context"
6
+ "gorm.io/gorm"
7
+ )
8
+
9
+ type CVRepository interface {
10
+ SaveAccountDetails(ctx context.Context, req *models.AccountDetails) (*models.AccountDetails, error)
11
+ GetAccountDetailsByAccountID(ctx context.Context, accountID int64) (*models.AccountDetails, error)
12
+
13
+ SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error)
14
+ GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error)
15
+
16
+ SaveFamilyMember(ctx context.Context, req *models.FamilyMemberCV) (*models.FamilyMemberCV, error)
17
+ ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error)
18
+ GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error)
19
+ DeleteFamilyMember(ctx context.Context, id int64) error
20
+
21
+ SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthCV) (*models.PhysicalAndHealthCV, error)
22
+ GetPhysicalAndHealthByAccountID(ctx context.Context, accountID int64) (*models.PhysicalAndHealthCV, error)
23
+
24
+ SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error)
25
+ GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error)
26
+ }
27
+
28
+ type cvRepository struct {
29
+ db *gorm.DB
30
+ }
31
+
32
+ func NewCVRepository(db *gorm.DB) CVRepository {
33
+ return &cvRepository{
34
+ db: db,
35
+ }
36
+ }
37
+
38
+ func (r *cvRepository) SaveAccountDetails(ctx context.Context, req *models.AccountDetails) (*models.AccountDetails, error) {
39
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
40
+ return req, err
41
+ }
42
+ return req, nil
43
+ }
44
+
45
+ func (r *cvRepository) GetAccountDetailsByAccountID(ctx context.Context, accountID int64) (*models.AccountDetails, error) {
46
+ var accountDetails models.AccountDetails
47
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&accountDetails).Error; err != nil {
48
+ return nil, err
49
+ }
50
+ return &accountDetails, nil
51
+ }
52
+
53
+ func (r *cvRepository) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error) {
54
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
55
+ return req, err
56
+ }
57
+ return req, nil
58
+ }
59
+
60
+ func (r *cvRepository) GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error) {
61
+ var personalityAndPreference models.PersonalityAndPreferenceCV
62
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&personalityAndPreference).Error; err != nil {
63
+ return nil, err
64
+ }
65
+ return &personalityAndPreference, nil
66
+ }
67
+
68
+ func (r *cvRepository) SaveFamilyMember(ctx context.Context, req *models.FamilyMemberCV) (*models.FamilyMemberCV, error) {
69
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
70
+ return req, err
71
+ }
72
+ return req, nil
73
+ }
74
+
75
+ func (r *cvRepository) GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error) {
76
+ var familyMember models.FamilyMemberCV
77
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&familyMember).Error; err != nil {
78
+ return nil, err
79
+ }
80
+ return &familyMember, nil
81
+ }
82
+
83
+ func (r *cvRepository) DeleteFamilyMember(ctx context.Context, id int64) error {
84
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.FamilyMemberCV{}).Error; err != nil {
85
+ return err
86
+ }
87
+ return nil
88
+ }
89
+
90
+ func (r *cvRepository) ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error) {
91
+ familyMembers := make([]models.FamilyMemberCV, 0)
92
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&familyMembers).Error; err != nil {
93
+ return nil, err
94
+ }
95
+ return familyMembers, nil
96
+ }
97
+
98
+ func (r *cvRepository) SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthCV) (*models.PhysicalAndHealthCV, error) {
99
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
100
+ return req, err
101
+ }
102
+ return req, nil
103
+ }
104
+
105
+ func (r *cvRepository) GetPhysicalAndHealthByAccountID(ctx context.Context, accountID int64) (*models.PhysicalAndHealthCV, error) {
106
+ var physicalAndHealth models.PhysicalAndHealthCV
107
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&physicalAndHealth).Error; err != nil {
108
+ return nil, err
109
+ }
110
+ return &physicalAndHealth, nil
111
+ }
112
+
113
+ func (r *cvRepository) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error) {
114
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
115
+ return req, err
116
+ }
117
+ return req, nil
118
+ }
119
+
120
+ func (r *cvRepository) GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error) {
121
+ var worshipAndReligiousUnderstanding models.WorshipAndReligiousUnderstandingCV
122
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&worshipAndReligiousUnderstanding).Error; err != nil {
123
+ return nil, err
124
+ }
125
+ return &worshipAndReligiousUnderstanding, nil
126
+ }
space/space/space/space/space/router/cv_route.go ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import "api.qobiltu.id/middleware"
4
+
5
+ func (s *Server) CVRoute() {
6
+ routerGroup := s.router.Group("/api/v1/cv").Use(middleware.AuthUser)
7
+ {
8
+ routerGroup.POST("/account-details", s.cvController.SaveAccountDetails)
9
+ routerGroup.GET("/account-details", s.cvController.GetAccountDetails)
10
+
11
+ routerGroup.POST("/personality-and-preferences", s.cvController.SavePersonalityAndPreference)
12
+ routerGroup.GET("/personality-and-preferences", s.cvController.GetPersonalityAndPreference)
13
+
14
+ routerGroup.POST("/family-members", s.cvController.CreateFamilyMember)
15
+ routerGroup.GET("/family-members", s.cvController.ListFamilyMember)
16
+ routerGroup.GET("/family-members/:id", s.cvController.GetFamilyMember)
17
+ routerGroup.PUT("/family-members/:id", s.cvController.UpdateFamilyMember)
18
+ routerGroup.DELETE("/family-members/:id", s.cvController.DeleteFamilyMember)
19
+
20
+ routerGroup.POST("/physical-and-healths", s.cvController.SavePhysicalAndHealth)
21
+ routerGroup.GET("/physical-and-healths", s.cvController.GetPhysicalAndHealth)
22
+
23
+ routerGroup.POST("/worship-and-religious-understandings", s.cvController.SaveWorshipAndReligiousUnderstanding)
24
+ routerGroup.GET("/worship-and-religious-understandings", s.cvController.GetWorshipAndReligiousUnderstanding)
25
+ }
26
+ }
space/space/space/space/space/router/router.go CHANGED
@@ -1,20 +1,21 @@
1
  package router
2
 
3
  import (
4
- "api.qobiltu.id/controller"
5
  )
6
 
7
  func (s *Server) setupRoutes() {
8
 
9
- s.router.GET("/", controller.HomeController)
10
 
11
- AuthRoute(s.router)
12
- UserRoute(s.router)
13
- EmailRoute(s.router)
14
- OptionsRoute(s.router)
15
- AcademyRoute(s.router)
16
- QuizRoute(s.router)
17
 
18
- // another way to register routes
19
- s.HealthCheckRoute()
 
20
  }
 
1
  package router
2
 
3
  import (
4
+ "api.qobiltu.id/controller"
5
  )
6
 
7
  func (s *Server) setupRoutes() {
8
 
9
+ s.router.GET("/", controller.HomeController)
10
 
11
+ AuthRoute(s.router)
12
+ UserRoute(s.router)
13
+ EmailRoute(s.router)
14
+ OptionsRoute(s.router)
15
+ AcademyRoute(s.router)
16
+ QuizRoute(s.router)
17
 
18
+ // another way to register routes
19
+ s.HealthCheckRoute()
20
+ s.CVRoute()
21
  }
space/space/space/space/space/router/server.go CHANGED
@@ -1,6 +1,7 @@
1
  package router
2
 
3
  import (
 
4
  "api.qobiltu.id/controller/health_check"
5
  "github.com/gin-gonic/gin"
6
  )
@@ -8,10 +9,12 @@ import (
8
  type Server struct {
9
  router *gin.Engine
10
  healthCheckController health_check_controller.HealthCheckController
 
11
  }
12
 
13
  func NewServer(
14
  healthCheckController health_check_controller.HealthCheckController,
 
15
  ) (*Server, error) {
16
 
17
  router := gin.Default()
@@ -19,6 +22,7 @@ func NewServer(
19
 
20
  server := &Server{
21
  healthCheckController: healthCheckController,
 
22
  router: router,
23
  }
24
 
 
1
  package router
2
 
3
  import (
4
+ cv_controller "api.qobiltu.id/controller/cv"
5
  "api.qobiltu.id/controller/health_check"
6
  "github.com/gin-gonic/gin"
7
  )
 
9
  type Server struct {
10
  router *gin.Engine
11
  healthCheckController health_check_controller.HealthCheckController
12
+ cvController cv_controller.CVController
13
  }
14
 
15
  func NewServer(
16
  healthCheckController health_check_controller.HealthCheckController,
17
+ cvController cv_controller.CVController,
18
  ) (*Server, error) {
19
 
20
  router := gin.Default()
 
22
 
23
  server := &Server{
24
  healthCheckController: healthCheckController,
25
+ cvController: cvController,
26
  router: router,
27
  }
28
 
space/space/space/space/space/services/cv_service.go ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ "api.qobiltu.id/repositories"
6
+ "api.qobiltu.id/response"
7
+ "context"
8
+ "errors"
9
+ "gorm.io/gorm"
10
+ )
11
+
12
+ type CVService interface {
13
+ SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error)
14
+ GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error)
15
+
16
+ SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error)
17
+ GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error)
18
+
19
+ CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
20
+ UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
21
+ ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error)
22
+ GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error)
23
+ DeleteFamilyMember(ctx context.Context, id int64) error
24
+
25
+ SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error)
26
+ GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error)
27
+
28
+ SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
29
+ GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error)
30
+ }
31
+
32
+ type cvService struct {
33
+ cvRepository repositories.CVRepository
34
+ }
35
+
36
+ func NewCVService(cvRepository repositories.CVRepository) CVService {
37
+ return &cvService{
38
+ cvRepository: cvRepository,
39
+ }
40
+ }
41
+
42
+ func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error) {
43
+ // Ambil data lama jika ada
44
+ accountDetails, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, req.AccountID)
45
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
46
+ return nil, response.HandleGormError(err, "Internal Server Error")
47
+ }
48
+
49
+ // Apply perubahan
50
+ if accountDetails == nil {
51
+ accountDetails = &models.AccountDetails{}
52
+ }
53
+
54
+ accountDetails.AccountID = uint(req.AccountID)
55
+ accountDetails.FullName = req.FullName
56
+ accountDetails.Gender = req.Gender
57
+ accountDetails.DateOfBirth = req.DateOfBirth
58
+ accountDetails.PlaceOfBirth = req.PlaceOfBirth
59
+ accountDetails.Domicile = req.Domicile
60
+ accountDetails.MaritalStatus = req.MaritalStatus
61
+ accountDetails.LastEducation = req.LastEducation
62
+ accountDetails.LastJob = req.LastJob
63
+
64
+ // Simpan data
65
+ res, err := s.cvRepository.SaveAccountDetails(ctx, accountDetails)
66
+ if err != nil {
67
+ return nil, response.HandleGormError(err, "Internal Server Error")
68
+ }
69
+
70
+ return res, nil
71
+ }
72
+
73
+ func (s *cvService) GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error) {
74
+ res, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, id)
75
+ if err != nil {
76
+ return nil, response.HandleGormError(err, "Data diri tidak ditemukan")
77
+ }
78
+ return res, nil
79
+ }
80
+
81
+ func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error) {
82
+ // Ambil data lama jika ada
83
+ personalityAndPreference, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, req.AccountID)
84
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
85
+ return nil, response.HandleGormError(err, "Internal Server Error")
86
+ }
87
+
88
+ // Apply perubahan
89
+ if personalityAndPreference == nil {
90
+ personalityAndPreference = &models.PersonalityAndPreferenceCV{}
91
+ }
92
+
93
+ personalityAndPreference.AccountID = req.AccountID
94
+ personalityAndPreference.PositiveTraits = req.PositiveTraits
95
+ personalityAndPreference.NegativeTraits = req.NegativeTraits
96
+ personalityAndPreference.Hobbies = req.Hobbies
97
+ personalityAndPreference.LifeGoals = req.LifeGoals
98
+ personalityAndPreference.DailyActivities = req.DailyActivities
99
+ personalityAndPreference.LeisureActivities = req.LeisureActivities
100
+ personalityAndPreference.Likes = req.Likes
101
+ personalityAndPreference.Dislikes = req.Dislikes
102
+ personalityAndPreference.StressHandling = req.StressHandling
103
+ personalityAndPreference.AngerTriggers = req.AngerTriggers
104
+ personalityAndPreference.FavoriteFoodAndDrinks = req.FavoriteFoodAndDrinks
105
+ personalityAndPreference.CanCook = req.CanCook
106
+ personalityAndPreference.TypesOfDishesCooked = req.TypesOfDishesCooked
107
+ personalityAndPreference.MonthlyExpenses = req.MonthlyExpenses
108
+
109
+ res, err := s.cvRepository.SavePersonalityAndPreference(ctx, personalityAndPreference)
110
+ if err != nil {
111
+ return nil, response.HandleGormError(err, "Internal Server Error")
112
+ }
113
+
114
+ return res, nil
115
+ }
116
+
117
+ func (s *cvService) GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error) {
118
+ res, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, id)
119
+ if err != nil {
120
+ return nil, response.HandleGormError(err, "Internal Server Error")
121
+ }
122
+
123
+ return res, nil
124
+ }
125
+
126
+ func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
127
+ // Mapping request ke model
128
+ familyMember := &models.FamilyMemberCV{
129
+ AccountID: req.AccountID,
130
+ Role: req.Role,
131
+ Status: req.Status,
132
+ Religion: req.Religion,
133
+ Job: req.Job,
134
+ LastEducation: req.LastEducation,
135
+ Age: req.Age,
136
+ }
137
+
138
+ // Simpan ke repository
139
+ res, err := s.cvRepository.SaveFamilyMember(ctx, familyMember)
140
+ if err != nil {
141
+ return nil, response.HandleGormError(err, "Gagal menyimpan anggota keluarga")
142
+ }
143
+
144
+ return res, nil
145
+ }
146
+
147
+ func (s *cvService) ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error) {
148
+ list, err := s.cvRepository.ListFamilyMember(ctx, accountID)
149
+ if err != nil {
150
+ return nil, response.HandleGormError(err, "Gagal mengambil daftar anggota keluarga")
151
+ }
152
+ return list, nil
153
+ }
154
+
155
+ func (s *cvService) GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error) {
156
+ res, err := s.cvRepository.GetFamilyMember(ctx, id)
157
+ if err != nil {
158
+ return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
159
+ }
160
+ return res, nil
161
+ }
162
+
163
+ func (s *cvService) DeleteFamilyMember(ctx context.Context, id int64) error {
164
+ err := s.cvRepository.DeleteFamilyMember(ctx, id)
165
+ if err != nil {
166
+ return response.HandleGormError(err, "Gagal menghapus anggota keluarga")
167
+ }
168
+ return nil
169
+ }
170
+
171
+ func (s *cvService) UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
172
+ existing, err := s.cvRepository.GetFamilyMember(ctx, id)
173
+ if err != nil {
174
+ return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
175
+ }
176
+
177
+ existing.Role = req.Role
178
+ existing.Status = req.Status
179
+ existing.Religion = req.Religion
180
+ existing.Job = req.Job
181
+ existing.LastEducation = req.LastEducation
182
+ existing.Age = req.Age
183
+
184
+ updated, err := s.cvRepository.SaveFamilyMember(ctx, existing)
185
+ if err != nil {
186
+ return nil, response.HandleGormError(err, "Gagal memperbarui anggota keluarga")
187
+ }
188
+
189
+ return updated, nil
190
+ }
191
+
192
+ func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
193
+ // Cek apakah data sudah ada berdasarkan account_id
194
+ existing, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, req.AccountID)
195
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
196
+ return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data fisik dan kesehatan")
197
+ }
198
+
199
+ // Jika belum ada, buat objek baru
200
+ if existing == nil {
201
+ existing = &models.PhysicalAndHealthCV{}
202
+ }
203
+
204
+ // Mapping field dari request
205
+ existing.AccountID = req.AccountID
206
+ existing.HeightInCm = req.HeightInCm
207
+ existing.WeightInKg = req.WeightInKg
208
+ existing.BodyShape = req.BodyShape
209
+ existing.SkinColor = req.SkinColor
210
+ existing.HairType = req.HairType
211
+ existing.MedicalHistory = req.MedicalHistory
212
+ existing.PhysicalDisorder = req.PhysicalDisorder
213
+ existing.PhysicalTraits = req.PhysicalTraits
214
+
215
+ // Simpan data
216
+ res, err := s.cvRepository.SavePhysicalAndHealth(ctx, existing)
217
+ if err != nil {
218
+ return nil, response.HandleGormError(err, "Gagal menyimpan data fisik dan kesehatan")
219
+ }
220
+
221
+ return res, nil
222
+ }
223
+
224
+ func (s *cvService) GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error) {
225
+ res, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, id)
226
+ if err != nil {
227
+ return nil, response.HandleGormError(err, "Data fisik dan kesehatan tidak ditemukan")
228
+ }
229
+ return res, nil
230
+ }
231
+
232
+ func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
233
+ // Cek apakah data sudah ada berdasarkan account_id
234
+ worshipAndReligiousUnderstanding, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, req.AccountID)
235
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
236
+ return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data agama dan pemahaman agama")
237
+ }
238
+
239
+ // Jika belum ada, buat objek baru
240
+ if worshipAndReligiousUnderstanding == nil {
241
+ worshipAndReligiousUnderstanding = &models.WorshipAndReligiousUnderstandingCV{}
242
+ }
243
+
244
+ // Mapping field dari request
245
+ worshipAndReligiousUnderstanding.AccountID = req.AccountID
246
+ worshipAndReligiousUnderstanding.ObligatoryPrayer = req.ObligatoryPrayer
247
+ worshipAndReligiousUnderstanding.CongregationalPrayer = req.CongregationalPrayer
248
+ worshipAndReligiousUnderstanding.TahajjudPrayer = req.TahajjudPrayer
249
+ worshipAndReligiousUnderstanding.DhuhaPrayer = req.DhuhaPrayer
250
+ worshipAndReligiousUnderstanding.QuranMemorization = req.QuranMemorization
251
+ worshipAndReligiousUnderstanding.QuranReadingAbility = req.QuranReadingAbility
252
+ worshipAndReligiousUnderstanding.DaudFasting = req.DaudFasting
253
+ worshipAndReligiousUnderstanding.AyyamulBidhFasting = req.AyyamulBidhFasting
254
+ worshipAndReligiousUnderstanding.HajjOrUmrah = req.HajjOrUmrah
255
+ worshipAndReligiousUnderstanding.ListeningToMusic = req.ListeningToMusic
256
+ worshipAndReligiousUnderstanding.OpinionOnIkhtilat = req.OpinionOnIkhtilat
257
+ worshipAndReligiousUnderstanding.OpinionOnTouchingNonMahram = req.OpinionOnTouchingNonMahram
258
+ worshipAndReligiousUnderstanding.OpinionOnVeil = req.OpinionOnVeil
259
+ worshipAndReligiousUnderstanding.WeeklyReligiousStudies = req.WeeklyReligiousStudies
260
+ worshipAndReligiousUnderstanding.FollowedUstadz = req.FollowedUstadz
261
+
262
+ // Simpan data
263
+ res, err := s.cvRepository.SaveWorshipAndReligiousUnderstanding(ctx, worshipAndReligiousUnderstanding)
264
+ if err != nil {
265
+ return nil, response.HandleGormError(err, "Internal Server Error")
266
+ }
267
+
268
+ return res, nil
269
+ }
270
+
271
+ func (s *cvService) GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error) {
272
+ res, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, id)
273
+ if err != nil {
274
+ return nil, response.HandleGormError(err, "Data agama dan pemahaman agama tidak ditemukan")
275
+ }
276
+ return res, nil
277
+ }
space/space/space/space/space/space/space/.gitignore CHANGED
@@ -6,3 +6,4 @@ README.md
6
  .error
7
  logs/
8
  .idea
 
 
6
  .error
7
  logs/
8
  .idea
9
+ my-notes
space/space/space/space/space/space/space/controller/health_check/health_check_controller.go CHANGED
@@ -3,29 +3,33 @@ package health_check_controller
3
  import (
4
  "api.qobiltu.id/models"
5
  "api.qobiltu.id/response"
6
- "api.qobiltu.id/services/health_check_service"
7
  "github.com/gin-gonic/gin"
8
  "net/http"
9
  )
10
 
11
- type HealthCheckController struct {
12
- healthCheckService *health_check_service.HealthCheckService
13
  }
14
 
15
- func NewHealthCheckController(healthCheckService *health_check_service.HealthCheckService) *HealthCheckController {
16
- return &HealthCheckController{
 
 
 
 
17
  healthCheckService: healthCheckService,
18
  }
19
  }
20
 
21
- func (h *HealthCheckController) Check(ctx *gin.Context) {
22
  req := models.HealthCheckRequest{}
23
 
24
- res, err := h.healthCheckService.Check(ctx, &req)
25
  if err != nil {
26
  response.HandleError(ctx, err)
27
  return
28
  }
29
 
30
- response.HandleSuccess(ctx, "Service is running", http.StatusOK, res, nil)
31
  }
 
3
  import (
4
  "api.qobiltu.id/models"
5
  "api.qobiltu.id/response"
6
+ "api.qobiltu.id/services"
7
  "github.com/gin-gonic/gin"
8
  "net/http"
9
  )
10
 
11
+ type HealthCheckController interface {
12
+ Check(ctx *gin.Context)
13
  }
14
 
15
+ type healthCheckController struct {
16
+ healthCheckService services.HealthCheckService
17
+ }
18
+
19
+ func NewHealthCheckController(healthCheckService services.HealthCheckService) HealthCheckController {
20
+ return &healthCheckController{
21
  healthCheckService: healthCheckService,
22
  }
23
  }
24
 
25
+ func (c *healthCheckController) Check(ctx *gin.Context) {
26
  req := models.HealthCheckRequest{}
27
 
28
+ res, err := c.healthCheckService.Check(ctx, &req)
29
  if err != nil {
30
  response.HandleError(ctx, err)
31
  return
32
  }
33
 
34
+ response.HandleSuccess(ctx, http.StatusOK, "Service OK", res, nil)
35
  }
space/space/space/space/space/space/space/main.go CHANGED
@@ -6,7 +6,7 @@ import (
6
  "api.qobiltu.id/mail"
7
  "api.qobiltu.id/repositories"
8
  "api.qobiltu.id/router"
9
- "api.qobiltu.id/services/health_check_service"
10
  "api.qobiltu.id/utils"
11
  "api.qobiltu.id/worker"
12
  "github.com/hibiken/asynq"
@@ -43,7 +43,7 @@ func main() {
43
 
44
  // setup repo, service, and controller
45
  healthCheckRepository := repositories.NewHealthCheckRepository(config.DB)
46
- healthCheckService := health_check_service.NewHealthCheckService(healthCheckRepository)
47
  healthCheckController := health_check_controller.NewHealthCheckController(healthCheckService)
48
 
49
  // start task processor
 
6
  "api.qobiltu.id/mail"
7
  "api.qobiltu.id/repositories"
8
  "api.qobiltu.id/router"
9
+ "api.qobiltu.id/services"
10
  "api.qobiltu.id/utils"
11
  "api.qobiltu.id/worker"
12
  "github.com/hibiken/asynq"
 
43
 
44
  // setup repo, service, and controller
45
  healthCheckRepository := repositories.NewHealthCheckRepository(config.DB)
46
+ healthCheckService := services.NewHealthCheckService(healthCheckRepository)
47
  healthCheckController := health_check_controller.NewHealthCheckController(healthCheckService)
48
 
49
  // start task processor
space/space/space/space/space/space/space/models/database_orm_model.go CHANGED
@@ -1,207 +1,207 @@
1
- package models
2
-
3
- import (
4
- "time"
5
-
6
- uuid "github.com/satori/go.uuid"
7
- )
8
-
9
- type Account struct {
10
- Id uint `gorm:"primaryKey" json:"id"`
11
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
12
- Email string `gorm:"uniqueIndex" json:"email"`
13
- Password string `json:"password"`
14
- IsEmailVerified bool `json:"is_email_verified"`
15
- IsDetailCompleted bool `json:"is_detail_completed"`
16
- CreatedAt time.Time `json:"created_at"`
17
- DeletedAt *time.Time `json:"deleted_at" gorm:"default:null"`
18
- }
19
-
20
- type AccountDetails struct {
21
- ID uint `gorm:"primaryKey" json:"id"`
22
- AccountID uint `json:"account_id"`
23
- InitialName string `json:"initial_name"`
24
- FullName *string `json:"full_name"`
25
- DateOfBirth *time.Time `json:"date_of_birth"`
26
- PlaceOfBirth *string `json:"place_of_birth"`
27
- Domicile *string `json:"domicile"`
28
- LastJob *string `json:"last_job"`
29
- Gender *bool `json:"gender"`
30
- LastEducation *string `json:"last_education"`
31
- MaritalStatus *string `json:"marital_status"`
32
- Avatar *string `json:"avatar"`
33
- PhoneNumber *string `json:"phone_number"`
34
- }
35
-
36
- type EmailVerification struct {
37
- ID uint `gorm:"primaryKey" json:"id"`
38
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
39
- Token uint `json:"token"`
40
- AccountID uint `json:"account_id"`
41
- IsExpired bool `json:"is_expired"`
42
- CreatedAt time.Time `json:"created_at"`
43
- ExpiredAt time.Time `json:"expired_at"`
44
- }
45
-
46
- type ExternalAuth struct {
47
- ID uint `gorm:"primaryKey" json:"id"`
48
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
49
- OauthID string `json:"oauth_id"`
50
- AccountID uint `json:"account_id"`
51
- OauthProvider string `json:"oauth_provider"`
52
- }
53
-
54
- type FCM struct {
55
- ID uint `gorm:"primaryKey" json:"id"`
56
- AccountID uint `json:"account_id"`
57
- FCMToken string `json:"fcm_token"`
58
- }
59
-
60
- type ForgotPassword struct {
61
- ID uint `gorm:"primaryKey" json:"id"`
62
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
63
- Token uint `json:"token"`
64
- AccountID uint `json:"account_id"`
65
- IsExpired bool `json:"is_expired"`
66
- CreatedAt time.Time `json:"created_at"`
67
- ExpiredAt time.Time `json:"expired_at"`
68
- }
69
-
70
- type Academy struct {
71
- ID uint `gorm:"primaryKey" json:"id"`
72
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
73
- Title string `json:"title"`
74
- Slug string `json:"slug" gorm:"uniqueIndex" `
75
- TotalMaterial int `json:"total_material"`
76
- CompletedMaterial int `json:"completed_material"`
77
- IsCompletedRead bool `json:"is_read"`
78
- IsPassedExam bool `json:"is_exam"`
79
- Description string `json:"description"`
80
- }
81
-
82
- type AcademyMaterial struct {
83
- ID uint `gorm:"primaryKey" json:"id"`
84
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
85
- AcademyID uint `json:"academy_id"`
86
- Title string `json:"title"`
87
- Slug string `json:"slug" gorm:"uniqueIndex"`
88
- IsCompleted bool `json:"is_completed"`
89
- Description string `json:"description"`
90
- }
91
-
92
- type AcademyContent struct {
93
- ID uint `gorm:"primaryKey" json:"id"`
94
- UUID uuid.UUID `json:"uuid"`
95
- Title string `json:"title"`
96
- Order uint `json:"order"`
97
- AcademyMaterialID uint `json:"academy_material_id"`
98
- Description string `json:"description"`
99
- }
100
- type OptionCategory struct {
101
- ID uint `gorm:"primaryKey" json:"id"`
102
- OptionName string `json:"option_name"`
103
- OptionSlug string `json:"option_slug" gorm:"uniqueIndex"`
104
- }
105
-
106
- type OptionValues struct {
107
- ID uint `gorm:"primaryKey" json:"id"`
108
- OptionCategoryID uint `json:"option_category_id"`
109
- OptionValue string `json:"option_value"`
110
- }
111
- type AcademyMaterialProgress struct {
112
- ID uint `gorm:"primaryKey" json:"id"`
113
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
114
- AccountID uint `json:"account_id"`
115
- AcademyMaterialID uint `json:"academy_material_id"`
116
- Progress uint `json:"progress"`
117
- }
118
-
119
- type AcademyContentProgress struct {
120
- ID uint `gorm:"primaryKey" json:"id"`
121
- UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
122
- AccountID uint `json:"account_id"`
123
- AcademyID uint `json:"academy_id"`
124
- }
125
-
126
- type RegionProvince struct {
127
- ID uint `json:"id"`
128
- Name string `json:"name"`
129
- Code string `json:"code"`
130
- }
131
-
132
- type RegionCity struct {
133
- ID uint `json:"id"`
134
- Type string `json:"type"`
135
- Name string `json:"name"`
136
- Code string `json:"code"`
137
- FullCode string `json:"full_code"`
138
- ProvinceID uint `json:"province_id"`
139
- }
140
- type Answer struct {
141
- ID uint `gorm:"primaryKey" json:"id"`
142
- QuestionID uint `json:"question_id"`
143
- Content string `json:"content"`
144
- IsCorrect bool `json:"-"`
145
- }
146
- type Question struct {
147
- ID uint `gorm:"primaryKey" json:"id"`
148
- QuizID uint `json:"quiz_id"`
149
- Content string `json:"content"`
150
- Order int `json:"order"`
151
- CorrectAnswer uint `json:"-"`
152
- }
153
- type Quiz struct {
154
- ID uint `gorm:"primaryKey" json:"id"`
155
- AcademyID uint `json:"academy_id"`
156
- Title string `json:"title"`
157
- Description string `json:"description"`
158
- AttemptLimit int `json:"attempt_limit"`
159
- TimeLimit int `json:"time_limit"`
160
- MinScore int `json:"min_score"`
161
- CreatedAt time.Time `json:"created_at"`
162
- }
163
-
164
- type QuizAttempt struct {
165
- ID uint `gorm:"primaryKey" json:"id"`
166
- AccountID uint `json:"user_id"`
167
- QuizID uint `json:"quiz_id"`
168
- StartedAt time.Time `json:"started_at"`
169
- DueAt time.Time `json:"due_at"`
170
- FinishedAt *time.Time `json:"finished_at"`
171
- Score float64 `json:"score"`
172
- }
173
- type UserAnswer struct {
174
- ID uint `gorm:"primaryKey" json:"id"`
175
- QuizAttemptID uint `json:"quiz_attempt_id"`
176
- QuestionID uint `json:"question_id"`
177
- SelectedAnswer uint `json:"selected_answer"`
178
- IsCorrect bool `json:"is_correct"`
179
- }
180
- type QuizResult struct {
181
- QuizAttemptID uint `gorm:"column:quiz_attempt_id" json:"quiz_attempt_id"`
182
- TotalQuestions int `gorm:"column:total_questions" json:"total_questions"`
183
- CorrectAnswers int `gorm:"column:correct_answers" json:"correct_answers"`
184
- AverageScore float64 `gorm:"column:average_score" json:"average_score"`
185
- }
186
-
187
- // Gorm table name settings
188
- func (Account) TableName() string { return "account" }
189
- func (AccountDetails) TableName() string { return "account_details" }
190
- func (EmailVerification) TableName() string { return "email_verifications" }
191
- func (ExternalAuth) TableName() string { return "extern_auth" }
192
- func (FCM) TableName() string { return "fcm" }
193
- func (ForgotPassword) TableName() string { return "forgot_password" }
194
- func (Academy) TableName() string { return "academy" }
195
- func (AcademyMaterial) TableName() string { return "academy_materials" }
196
- func (AcademyContent) TableName() string { return "academy_contents" }
197
- func (AcademyMaterialProgress) TableName() string { return "academy_materials_progress" }
198
- func (AcademyContentProgress) TableName() string { return "academy_contents_progress" }
199
- func (RegionProvince) TableName() string { return "region_provinces" }
200
- func (RegionCity) TableName() string { return "region_cities" }
201
- func (Answer) TableName() string { return "answers" }
202
- func (Question) TableName() string { return "questions" }
203
- func (Quiz) TableName() string { return "quizzes" }
204
- func (QuizAttempt) TableName() string { return "quiz_attempts" }
205
- func (UserAnswer) TableName() string { return "user_answers" }
206
- func (OptionCategory) TableName() string { return "option_categories" }
207
- func (OptionValues) TableName() string { return "option_values" }
 
1
+ package models
2
+
3
+ import (
4
+ "time"
5
+
6
+ uuid "github.com/satori/go.uuid"
7
+ )
8
+
9
+ type Account struct {
10
+ Id uint `gorm:"primaryKey" json:"id"`
11
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
12
+ Email string `gorm:"uniqueIndex" json:"email"`
13
+ Password string `json:"password"`
14
+ IsEmailVerified bool `json:"is_email_verified"`
15
+ IsDetailCompleted bool `json:"is_detail_completed"`
16
+ CreatedAt time.Time `json:"created_at"`
17
+ DeletedAt *time.Time `json:"deleted_at" gorm:"default:null"`
18
+ }
19
+
20
+ type AccountDetails struct {
21
+ ID uint64 `gorm:"primaryKey" json:"id"`
22
+ AccountID uint `json:"account_id"`
23
+ InitialName string `json:"initial_name"`
24
+ FullName *string `json:"full_name"`
25
+ DateOfBirth *time.Time `json:"date_of_birth"`
26
+ PlaceOfBirth *string `json:"place_of_birth"`
27
+ Domicile *string `json:"domicile"`
28
+ LastJob *string `json:"last_job"`
29
+ Gender *string `json:"gender"`
30
+ LastEducation *string `json:"last_education"`
31
+ MaritalStatus *string `json:"marital_status"`
32
+ Avatar *string `json:"avatar"`
33
+ PhoneNumber *string `json:"phone_number"`
34
+ }
35
+
36
+ type EmailVerification struct {
37
+ ID uint `gorm:"primaryKey" json:"id"`
38
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
39
+ Token uint `json:"token"`
40
+ AccountID uint `json:"account_id"`
41
+ IsExpired bool `json:"is_expired"`
42
+ CreatedAt time.Time `json:"created_at"`
43
+ ExpiredAt time.Time `json:"expired_at"`
44
+ }
45
+
46
+ type ExternalAuth struct {
47
+ ID uint `gorm:"primaryKey" json:"id"`
48
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
49
+ OauthID string `json:"oauth_id"`
50
+ AccountID uint `json:"account_id"`
51
+ OauthProvider string `json:"oauth_provider"`
52
+ }
53
+
54
+ type FCM struct {
55
+ ID uint `gorm:"primaryKey" json:"id"`
56
+ AccountID uint `json:"account_id"`
57
+ FCMToken string `json:"fcm_token"`
58
+ }
59
+
60
+ type ForgotPassword struct {
61
+ ID uint `gorm:"primaryKey" json:"id"`
62
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
63
+ Token uint `json:"token"`
64
+ AccountID uint `json:"account_id"`
65
+ IsExpired bool `json:"is_expired"`
66
+ CreatedAt time.Time `json:"created_at"`
67
+ ExpiredAt time.Time `json:"expired_at"`
68
+ }
69
+
70
+ type Academy struct {
71
+ ID uint `gorm:"primaryKey" json:"id"`
72
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
73
+ Title string `json:"title"`
74
+ Slug string `json:"slug" gorm:"uniqueIndex" `
75
+ TotalMaterial int `json:"total_material"`
76
+ CompletedMaterial int `json:"completed_material"`
77
+ IsCompletedRead bool `json:"is_read"`
78
+ IsPassedExam bool `json:"is_exam"`
79
+ Description string `json:"description"`
80
+ }
81
+
82
+ type AcademyMaterial struct {
83
+ ID uint `gorm:"primaryKey" json:"id"`
84
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
85
+ AcademyID uint `json:"academy_id"`
86
+ Title string `json:"title"`
87
+ Slug string `json:"slug" gorm:"uniqueIndex"`
88
+ IsCompleted bool `json:"is_completed"`
89
+ Description string `json:"description"`
90
+ }
91
+
92
+ type AcademyContent struct {
93
+ ID uint `gorm:"primaryKey" json:"id"`
94
+ UUID uuid.UUID `json:"uuid"`
95
+ Title string `json:"title"`
96
+ Order uint `json:"order"`
97
+ AcademyMaterialID uint `json:"academy_material_id"`
98
+ Description string `json:"description"`
99
+ }
100
+ type OptionCategory struct {
101
+ ID uint `gorm:"primaryKey" json:"id"`
102
+ OptionName string `json:"option_name"`
103
+ OptionSlug string `json:"option_slug" gorm:"uniqueIndex"`
104
+ }
105
+
106
+ type OptionValues struct {
107
+ ID uint `gorm:"primaryKey" json:"id"`
108
+ OptionCategoryID uint `json:"option_category_id"`
109
+ OptionValue string `json:"option_value"`
110
+ }
111
+ type AcademyMaterialProgress struct {
112
+ ID uint `gorm:"primaryKey" json:"id"`
113
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
114
+ AccountID uint `json:"account_id"`
115
+ AcademyMaterialID uint `json:"academy_material_id"`
116
+ Progress uint `json:"progress"`
117
+ }
118
+
119
+ type AcademyContentProgress struct {
120
+ ID uint `gorm:"primaryKey" json:"id"`
121
+ UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
122
+ AccountID uint `json:"account_id"`
123
+ AcademyID uint `json:"academy_id"`
124
+ }
125
+
126
+ type RegionProvince struct {
127
+ ID uint `json:"id"`
128
+ Name string `json:"name"`
129
+ Code string `json:"code"`
130
+ }
131
+
132
+ type RegionCity struct {
133
+ ID uint `json:"id"`
134
+ Type string `json:"type"`
135
+ Name string `json:"name"`
136
+ Code string `json:"code"`
137
+ FullCode string `json:"full_code"`
138
+ ProvinceID uint `json:"province_id"`
139
+ }
140
+ type Answer struct {
141
+ ID uint `gorm:"primaryKey" json:"id"`
142
+ QuestionID uint `json:"question_id"`
143
+ Content string `json:"content"`
144
+ IsCorrect bool `json:"-"`
145
+ }
146
+ type Question struct {
147
+ ID uint `gorm:"primaryKey" json:"id"`
148
+ QuizID uint `json:"quiz_id"`
149
+ Content string `json:"content"`
150
+ Order int `json:"order"`
151
+ CorrectAnswer uint `json:"-"`
152
+ }
153
+ type Quiz struct {
154
+ ID uint `gorm:"primaryKey" json:"id"`
155
+ AcademyID uint `json:"academy_id"`
156
+ Title string `json:"title"`
157
+ Description string `json:"description"`
158
+ AttemptLimit int `json:"attempt_limit"`
159
+ TimeLimit int `json:"time_limit"`
160
+ MinScore int `json:"min_score"`
161
+ CreatedAt time.Time `json:"created_at"`
162
+ }
163
+
164
+ type QuizAttempt struct {
165
+ ID uint `gorm:"primaryKey" json:"id"`
166
+ AccountID uint `json:"user_id"`
167
+ QuizID uint `json:"quiz_id"`
168
+ StartedAt time.Time `json:"started_at"`
169
+ DueAt time.Time `json:"due_at"`
170
+ FinishedAt *time.Time `json:"finished_at"`
171
+ Score float64 `json:"score"`
172
+ }
173
+ type UserAnswer struct {
174
+ ID uint `gorm:"primaryKey" json:"id"`
175
+ QuizAttemptID uint `json:"quiz_attempt_id"`
176
+ QuestionID uint `json:"question_id"`
177
+ SelectedAnswer uint `json:"selected_answer"`
178
+ IsCorrect bool `json:"is_correct"`
179
+ }
180
+ type QuizResult struct {
181
+ QuizAttemptID uint `gorm:"column:quiz_attempt_id" json:"quiz_attempt_id"`
182
+ TotalQuestions int `gorm:"column:total_questions" json:"total_questions"`
183
+ CorrectAnswers int `gorm:"column:correct_answers" json:"correct_answers"`
184
+ AverageScore float64 `gorm:"column:average_score" json:"average_score"`
185
+ }
186
+
187
+ // Gorm table name settings
188
+ func (Account) TableName() string { return "account" }
189
+ func (AccountDetails) TableName() string { return "account_details" }
190
+ func (EmailVerification) TableName() string { return "email_verifications" }
191
+ func (ExternalAuth) TableName() string { return "extern_auth" }
192
+ func (FCM) TableName() string { return "fcm" }
193
+ func (ForgotPassword) TableName() string { return "forgot_password" }
194
+ func (Academy) TableName() string { return "academy" }
195
+ func (AcademyMaterial) TableName() string { return "academy_materials" }
196
+ func (AcademyContent) TableName() string { return "academy_contents" }
197
+ func (AcademyMaterialProgress) TableName() string { return "academy_materials_progress" }
198
+ func (AcademyContentProgress) TableName() string { return "academy_contents_progress" }
199
+ func (RegionProvince) TableName() string { return "region_provinces" }
200
+ func (RegionCity) TableName() string { return "region_cities" }
201
+ func (Answer) TableName() string { return "answers" }
202
+ func (Question) TableName() string { return "questions" }
203
+ func (Quiz) TableName() string { return "quizzes" }
204
+ func (QuizAttempt) TableName() string { return "quiz_attempts" }
205
+ func (UserAnswer) TableName() string { return "user_answers" }
206
+ func (OptionCategory) TableName() string { return "option_categories" }
207
+ func (OptionValues) TableName() string { return "option_values" }
space/space/space/space/space/space/space/models/exception_model.go CHANGED
@@ -1,17 +1,23 @@
1
  package models
2
 
3
  type Exception struct {
4
- Unauthorized bool `json:"unauthorized,omitempty"`
5
- BadRequest bool `json:"bad_request,omitempty"`
6
- DataNotFound bool `json:"data_not_found,omitempty"`
7
- InternalServerError bool `json:"internal_server_error,omitempty"`
8
- DataDuplicate bool `json:"data_duplicate,omitempty"`
9
- QueryError bool `json:"query_error,omitempty"`
10
- InvalidPasswordLength bool `json:"invalid_password_length,omitempty"`
11
- IsPassTheLimit bool `json:"is_pass_the_limit,omitempty"`
12
- IsTimeOut bool `json:"is_time_out,omitempty"`
13
- AttemptNotFound bool `json:"attempt_not_found,omitempty"`
14
- Forbidden bool `json:"forbidden,omitempty"`
15
- ValidationError bool `json:"validation_error,omitempty"`
16
- Message string `json:"message,omitempty"`
 
 
 
 
 
 
17
  }
 
1
  package models
2
 
3
  type Exception struct {
4
+ Unauthorized bool `json:"unauthorized,omitempty"`
5
+ BadRequest bool `json:"bad_request,omitempty"`
6
+ DataNotFound bool `json:"data_not_found,omitempty"`
7
+ InternalServerError bool `json:"internal_server_error,omitempty"`
8
+ DataDuplicate bool `json:"data_duplicate,omitempty"`
9
+ QueryError bool `json:"query_error,omitempty"`
10
+ InvalidPasswordLength bool `json:"invalid_password_length,omitempty"`
11
+ IsPassTheLimit bool `json:"is_pass_the_limit,omitempty"`
12
+ IsTimeOut bool `json:"is_time_out,omitempty"`
13
+ AttemptNotFound bool `json:"attempt_not_found,omitempty"`
14
+ Forbidden bool `json:"forbidden,omitempty"`
15
+ ValidationError bool `json:"validation_error,omitempty"`
16
+
17
+ Message string `json:"message,omitempty"`
18
+ Err error `json:"-"`
19
+ }
20
+
21
+ func (a Exception) Error() string {
22
+ return a.Err.Error()
23
  }
space/space/space/space/space/space/space/models/health_check_model.go CHANGED
@@ -1,8 +1,6 @@
1
  package models
2
 
3
- type HealthCheckRequest struct {
4
- ExampleCallback func() (string, string)
5
- }
6
 
7
  type HealthCheckResponse struct {
8
  DatabaseStatus string `json:"database_status"`
 
1
  package models
2
 
3
+ type HealthCheckRequest struct{}
 
 
4
 
5
  type HealthCheckResponse struct {
6
  DatabaseStatus string `json:"database_status"`
space/space/space/space/space/space/space/repositories/health_check_repository.go CHANGED
@@ -7,29 +7,25 @@ import (
7
  "gorm.io/gorm"
8
  )
9
 
10
- type HealthCheckRepository struct {
 
 
 
 
11
  db *gorm.DB
12
  }
13
 
14
- func NewHealthCheckRepository(db *gorm.DB) *HealthCheckRepository {
15
- return &HealthCheckRepository{
16
  db: db,
17
  }
18
  }
19
 
20
- func (r *HealthCheckRepository) Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error) {
21
- res := &models.HealthCheckResponse{}
22
-
23
  err := config.RunTx(ctx, r.db, func(tx *gorm.DB) error {
24
  if err := tx.Exec("SELECT 1").Error; err != nil {
25
  return err
26
  }
27
-
28
- // call logic from service if needed
29
- // instead of writing the logic in the repository
30
- // and make sure the param consist of the ctx and request
31
- res.DatabaseStatus, res.RedisStatus = req.ExampleCallback()
32
-
33
  return nil
34
  })
35
 
@@ -37,5 +33,8 @@ func (r *HealthCheckRepository) Check(ctx context.Context, req *models.HealthChe
37
  return nil, err
38
  }
39
 
40
- return res, nil
 
 
 
41
  }
 
7
  "gorm.io/gorm"
8
  )
9
 
10
+ type HealthCheckRepository interface {
11
+ Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error)
12
+ }
13
+
14
+ type healthCheckRepository struct {
15
  db *gorm.DB
16
  }
17
 
18
+ func NewHealthCheckRepository(db *gorm.DB) HealthCheckRepository {
19
+ return &healthCheckRepository{
20
  db: db,
21
  }
22
  }
23
 
24
+ func (r *healthCheckRepository) Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error) {
 
 
25
  err := config.RunTx(ctx, r.db, func(tx *gorm.DB) error {
26
  if err := tx.Exec("SELECT 1").Error; err != nil {
27
  return err
28
  }
 
 
 
 
 
 
29
  return nil
30
  })
31
 
 
33
  return nil, err
34
  }
35
 
36
+ return &models.HealthCheckResponse{
37
+ DatabaseStatus: "OK",
38
+ RedisStatus: "OK",
39
+ }, nil
40
  }
space/space/space/space/space/space/space/response/api_response_v2.go ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package response
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ "api.qobiltu.id/utils"
6
+ "errors"
7
+ "net/http"
8
+
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func HandleError(c *gin.Context, err error) {
13
+ var exception models.Exception
14
+
15
+ if errors.As(err, &exception) {
16
+ utils.LogError(exception.Err)
17
+
18
+ switch {
19
+ case exception.DataDuplicate:
20
+ responseError(c, http.StatusConflict, exception)
21
+ case exception.Unauthorized:
22
+ responseError(c, http.StatusUnauthorized, exception)
23
+ case exception.DataNotFound:
24
+ responseError(c, http.StatusNotFound, exception)
25
+ case exception.Forbidden:
26
+ responseError(c, http.StatusForbidden, exception)
27
+ case exception.BadRequest:
28
+ responseError(c, http.StatusBadRequest, exception)
29
+ case exception.InternalServerError:
30
+ responseError(c, http.StatusInternalServerError, exception)
31
+ case exception.QueryError:
32
+ responseError(c, http.StatusInternalServerError, exception)
33
+ case exception.InvalidPasswordLength:
34
+ responseError(c, http.StatusBadRequest, exception)
35
+ case exception.IsPassTheLimit:
36
+ responseError(c, http.StatusTooManyRequests, exception)
37
+ case exception.IsTimeOut:
38
+ responseError(c, http.StatusRequestTimeout, exception)
39
+ case exception.AttemptNotFound:
40
+ responseError(c, http.StatusNotFound, exception)
41
+ case exception.ValidationError:
42
+ responseError(c, http.StatusUnprocessableEntity, exception)
43
+ default:
44
+ responseError(c, http.StatusInternalServerError, exception)
45
+ }
46
+ } else {
47
+ utils.LogError(err)
48
+ responseError(c, http.StatusInternalServerError, models.Exception{
49
+ InternalServerError: true,
50
+ Message: "Internal Server Error",
51
+ })
52
+ }
53
+ }
54
+
55
+ func HandleSuccess(c *gin.Context, status int, msg string, data any, metaData any) {
56
+ res := models.SuccessResponse{
57
+ Status: "success",
58
+ Message: msg,
59
+ Data: data,
60
+ MetaData: metaData,
61
+ }
62
+
63
+ c.JSON(status, res)
64
+ return
65
+ }
66
+
67
+ func responseError(c *gin.Context, status int, exception models.Exception) {
68
+ message := exception.Message
69
+ exception.Message = ""
70
+
71
+ res := models.ErrorResponse{
72
+ Status: "error",
73
+ Message: message,
74
+ Errors: exception,
75
+ }
76
+
77
+ c.AbortWithStatusJSON(status, res)
78
+ return
79
+ }
space/space/space/space/space/space/space/response/gorm.go ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package response
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ "errors"
6
+ "strings"
7
+
8
+ "gorm.io/gorm"
9
+ )
10
+
11
+ func HandleGormError(err error, fallbackMessage string) error {
12
+ if err == nil {
13
+ return nil
14
+ }
15
+
16
+ if errors.Is(err, gorm.ErrRecordNotFound) {
17
+ return models.Exception{
18
+ Message: "Data not found",
19
+ DataNotFound: true,
20
+ Err: err,
21
+ }
22
+ }
23
+
24
+ lowerErr := strings.ToLower(err.Error())
25
+ if strings.Contains(lowerErr, "duplicated key") || strings.Contains(lowerErr, "unique constraint") || strings.Contains(lowerErr, "duplicate entry") {
26
+ return models.Exception{
27
+ Message: "Data already exists",
28
+ DataDuplicate: true,
29
+ Err: err,
30
+ }
31
+ }
32
+
33
+ if strings.Contains(lowerErr, "password") && strings.Contains(lowerErr, "length") {
34
+ return models.Exception{
35
+ Message: "Invalid password length",
36
+ InvalidPasswordLength: true,
37
+ Err: err,
38
+ }
39
+ }
40
+
41
+ if strings.Contains(lowerErr, "permission denied") || strings.Contains(lowerErr, "forbidden") {
42
+ return models.Exception{
43
+ Message: "Access forbidden",
44
+ Forbidden: true,
45
+ Err: err,
46
+ }
47
+ }
48
+
49
+ if errors.As(err, &gorm.ErrInvalidData) {
50
+ return models.Exception{
51
+ Message: "Invalid data format",
52
+ BadRequest: true,
53
+ Err: err,
54
+ }
55
+ }
56
+
57
+ return models.Exception{
58
+ Message: fallbackMessage,
59
+ InternalServerError: true,
60
+ Err: err,
61
+ }
62
+ }
space/space/space/space/space/space/space/router/server.go CHANGED
@@ -7,11 +7,11 @@ import (
7
 
8
  type Server struct {
9
  router *gin.Engine
10
- healthCheckController *health_check_controller.HealthCheckController
11
  }
12
 
13
  func NewServer(
14
- healthCheckController *health_check_controller.HealthCheckController,
15
  ) (*Server, error) {
16
 
17
  router := gin.Default()
 
7
 
8
  type Server struct {
9
  router *gin.Engine
10
+ healthCheckController health_check_controller.HealthCheckController
11
  }
12
 
13
  func NewServer(
14
+ healthCheckController health_check_controller.HealthCheckController,
15
  ) (*Server, error) {
16
 
17
  router := gin.Default()
space/space/space/space/space/space/space/services/health_check_service.go ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ "api.qobiltu.id/repositories"
6
+ "api.qobiltu.id/response"
7
+ "context"
8
+ )
9
+
10
+ type HealthCheckService interface {
11
+ Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error)
12
+ }
13
+
14
+ type healthCheckService struct {
15
+ healthCheckRepository repositories.HealthCheckRepository
16
+ }
17
+
18
+ func NewHealthCheckService(healthCheckRepository repositories.HealthCheckRepository) HealthCheckService {
19
+ return &healthCheckService{
20
+ healthCheckRepository: healthCheckRepository,
21
+ }
22
+ }
23
+
24
+ func (s *healthCheckService) Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error) {
25
+ res, err := s.healthCheckRepository.Check(ctx, req)
26
+ if err != nil {
27
+ return nil, response.HandleGormError(err, "Internal Server Error")
28
+ }
29
+
30
+ return res, nil
31
+ }
space/space/space/space/space/space/space/services/user_profile_service.go CHANGED
@@ -1,115 +1,115 @@
1
- package services
2
-
3
- import (
4
- "regexp"
5
- "strconv"
6
- "strings"
7
-
8
- "api.qobiltu.id/models"
9
- "api.qobiltu.id/repositories"
10
- )
11
-
12
- type UserProfileService struct {
13
- Service[models.AccountDetails, models.UserProfileResponse]
14
- }
15
-
16
- // SanitizePhoneNumber membersihkan dan menormalkan nomor telepon ke format +62
17
- func SanitizePhoneNumber(input string) string {
18
- // Hilangkan semua spasi dan strip
19
- input = strings.ReplaceAll(input, " ", "")
20
- input = strings.ReplaceAll(input, "-", "")
21
- input = strings.ReplaceAll(input, "(", "")
22
- input = strings.ReplaceAll(input, ")", "")
23
-
24
- // Hilangkan semua karakter non-digit kecuali +
25
- re := regexp.MustCompile(`[^0-9\+]`)
26
- input = re.ReplaceAllString(input, "")
27
-
28
- // Handle nomor diawali 0 (contoh: 0812...) menjadi +62812...
29
- if strings.HasPrefix(input, "0") {
30
- input = "+62" + input[1:]
31
- }
32
-
33
- // Handle jika diawali dengan 62 tanpa + (contoh: 62812...)
34
- if strings.HasPrefix(input, "62") && !strings.HasPrefix(input, "+62") {
35
- input = "+" + input
36
- }
37
-
38
- // Handle jika tidak ada awalan +62 sama sekali (contoh: 8123456789)
39
- if !strings.HasPrefix(input, "+62") {
40
- if strings.HasPrefix(input, "8") {
41
- input = "+62" + input
42
- }
43
- }
44
-
45
- return input
46
- }
47
- func (s *UserProfileService) Create() {
48
- userProfile := repositories.CreateAccountDetails(s.Constructor)
49
- s.Error = userProfile.RowsError
50
- if userProfile.NoRecord {
51
- s.Exception.DataNotFound = true
52
- s.Exception.Message = "There is no account with given credentials!"
53
- return
54
- }
55
- s.Result = models.UserProfileResponse{
56
- Account: repositories.GetAccountById(s.Constructor.AccountID).Result,
57
- Details: userProfile.Result,
58
- }
59
- }
60
- func (s *UserProfileService) Retrieve() {
61
- userProfile := repositories.GetDetailAccountById(s.Constructor.AccountID)
62
- s.Error = userProfile.RowsError
63
- if userProfile.NoRecord {
64
- s.Exception.DataNotFound = true
65
- s.Exception.Message = "There is no account with given credentials!"
66
- return
67
- }
68
- s.Result = models.UserProfileResponse{
69
- Account: repositories.GetAccountById(s.Constructor.AccountID).Result,
70
- Details: userProfile.Result,
71
- }
72
- s.Result.Account.Password = "SECRET"
73
- }
74
-
75
- func (s *UserProfileService) Update() {
76
- if s.Constructor.PhoneNumber != nil {
77
- phoneNumber := *s.Constructor.PhoneNumber
78
- *s.Constructor.PhoneNumber = SanitizePhoneNumber(phoneNumber)
79
- }
80
- usersCount := repositories.GetAllAccount().RowsCount
81
- var initialName string
82
- if s.Constructor.Gender != nil {
83
- if *s.Constructor.Gender {
84
- initialName = "IKH_"
85
- } else {
86
- initialName = "AKH_"
87
- }
88
- }
89
-
90
- initialName += strconv.Itoa(usersCount)
91
- s.Constructor.InitialName = initialName
92
- userProfile := repositories.UpdateAccountDetails(s.Constructor)
93
- s.Error = userProfile.RowsError
94
- if userProfile.NoRecord {
95
- s.Exception.DataNotFound = true
96
- s.Exception.Message = "There is no account with given credentials!"
97
- return
98
- }
99
- account := repositories.GetAccountById(s.Constructor.AccountID)
100
- account.Result.IsDetailCompleted = (userProfile.Result.InitialName != "" &&
101
- userProfile.Result.FullName != nil &&
102
- userProfile.Result.DateOfBirth != nil &&
103
- userProfile.Result.PlaceOfBirth != nil &&
104
- userProfile.Result.Domicile != nil &&
105
- userProfile.Result.LastJob != nil &&
106
- userProfile.Result.Gender != nil &&
107
- userProfile.Result.LastEducation != nil &&
108
- userProfile.Result.MaritalStatus != nil)
109
- repositories.UpdateAccount(account.Result)
110
- s.Result = models.UserProfileResponse{
111
- Account: account.Result,
112
- Details: userProfile.Result,
113
- }
114
- s.Result.Account.Password = "SECRET"
115
- }
 
1
+ package services
2
+
3
+ import (
4
+ "regexp"
5
+ "strconv"
6
+ "strings"
7
+
8
+ "api.qobiltu.id/models"
9
+ "api.qobiltu.id/repositories"
10
+ )
11
+
12
+ type UserProfileService struct {
13
+ Service[models.AccountDetails, models.UserProfileResponse]
14
+ }
15
+
16
+ // SanitizePhoneNumber membersihkan dan menormalkan nomor telepon ke format +62
17
+ func SanitizePhoneNumber(input string) string {
18
+ // Hilangkan semua spasi dan strip
19
+ input = strings.ReplaceAll(input, " ", "")
20
+ input = strings.ReplaceAll(input, "-", "")
21
+ input = strings.ReplaceAll(input, "(", "")
22
+ input = strings.ReplaceAll(input, ")", "")
23
+
24
+ // Hilangkan semua karakter non-digit kecuali +
25
+ re := regexp.MustCompile(`[^0-9\+]`)
26
+ input = re.ReplaceAllString(input, "")
27
+
28
+ // Handle nomor diawali 0 (contoh: 0812...) menjadi +62812...
29
+ if strings.HasPrefix(input, "0") {
30
+ input = "+62" + input[1:]
31
+ }
32
+
33
+ // Handle jika diawali dengan 62 tanpa + (contoh: 62812...)
34
+ if strings.HasPrefix(input, "62") && !strings.HasPrefix(input, "+62") {
35
+ input = "+" + input
36
+ }
37
+
38
+ // Handle jika tidak ada awalan +62 sama sekali (contoh: 8123456789)
39
+ if !strings.HasPrefix(input, "+62") {
40
+ if strings.HasPrefix(input, "8") {
41
+ input = "+62" + input
42
+ }
43
+ }
44
+
45
+ return input
46
+ }
47
+ func (s *UserProfileService) Create() {
48
+ userProfile := repositories.CreateAccountDetails(s.Constructor)
49
+ s.Error = userProfile.RowsError
50
+ if userProfile.NoRecord {
51
+ s.Exception.DataNotFound = true
52
+ s.Exception.Message = "There is no account with given credentials!"
53
+ return
54
+ }
55
+ s.Result = models.UserProfileResponse{
56
+ Account: repositories.GetAccountById(s.Constructor.AccountID).Result,
57
+ Details: userProfile.Result,
58
+ }
59
+ }
60
+ func (s *UserProfileService) Retrieve() {
61
+ userProfile := repositories.GetDetailAccountById(s.Constructor.AccountID)
62
+ s.Error = userProfile.RowsError
63
+ if userProfile.NoRecord {
64
+ s.Exception.DataNotFound = true
65
+ s.Exception.Message = "There is no account with given credentials!"
66
+ return
67
+ }
68
+ s.Result = models.UserProfileResponse{
69
+ Account: repositories.GetAccountById(s.Constructor.AccountID).Result,
70
+ Details: userProfile.Result,
71
+ }
72
+ s.Result.Account.Password = "SECRET"
73
+ }
74
+
75
+ func (s *UserProfileService) Update() {
76
+ if s.Constructor.PhoneNumber != nil {
77
+ phoneNumber := *s.Constructor.PhoneNumber
78
+ *s.Constructor.PhoneNumber = SanitizePhoneNumber(phoneNumber)
79
+ }
80
+ usersCount := repositories.GetAllAccount().RowsCount
81
+ var initialName string
82
+ if s.Constructor.Gender != nil {
83
+ if strings.ToLower(*s.Constructor.Gender) == "laki-laki" {
84
+ initialName = "IKH_"
85
+ } else {
86
+ initialName = "AKH_"
87
+ }
88
+ }
89
+
90
+ initialName += strconv.Itoa(usersCount)
91
+ s.Constructor.InitialName = initialName
92
+ userProfile := repositories.UpdateAccountDetails(s.Constructor)
93
+ s.Error = userProfile.RowsError
94
+ if userProfile.NoRecord {
95
+ s.Exception.DataNotFound = true
96
+ s.Exception.Message = "There is no account with given credentials!"
97
+ return
98
+ }
99
+ account := repositories.GetAccountById(s.Constructor.AccountID)
100
+ account.Result.IsDetailCompleted = (userProfile.Result.InitialName != "" &&
101
+ userProfile.Result.FullName != nil &&
102
+ userProfile.Result.DateOfBirth != nil &&
103
+ userProfile.Result.PlaceOfBirth != nil &&
104
+ userProfile.Result.Domicile != nil &&
105
+ userProfile.Result.LastJob != nil &&
106
+ userProfile.Result.Gender != nil &&
107
+ userProfile.Result.LastEducation != nil &&
108
+ userProfile.Result.MaritalStatus != nil)
109
+ repositories.UpdateAccount(account.Result)
110
+ s.Result = models.UserProfileResponse{
111
+ Account: account.Result,
112
+ Details: userProfile.Result,
113
+ }
114
+ s.Result.Account.Password = "SECRET"
115
+ }
space/space/space/space/space/space/space/space/Dockerfile CHANGED
@@ -25,6 +25,12 @@ FROM alpine:latest
25
  # Set working directory
26
  WORKDIR /app
27
 
 
 
 
 
 
 
28
  # Copy hasil build dari builder ke image runtime
29
  COPY --from=builder /app/main .
30
 
 
25
  # Set working directory
26
  WORKDIR /app
27
 
28
+ # Install tzdata
29
+ RUN apk update && apk add --no-cache tzdata
30
+
31
+ # Set the timezone environment variable
32
+ ENV TZ="Asia/Jakarta"
33
+
34
  # Copy hasil build dari builder ke image runtime
35
  COPY --from=builder /app/main .
36