lifedebugger commited on
Commit
9e52285
·
1 Parent(s): 9adc91d

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. config/database_connection_config.go +3 -0
  2. controller/cv/cv_controller.go +210 -0
  3. controller/user/user_profile_controller.go +21 -21
  4. controller/user/user_update_profile_controller.go +25 -25
  5. models/database_orm_model.go +116 -56
  6. models/request_model.go +52 -20
  7. repositories/account_repository.go +87 -87
  8. repositories/cv_repository.go +107 -18
  9. router/cv_route.go +20 -5
  10. services/cv_service.go +264 -95
  11. services/external_authentication_service.go +81 -81
  12. services/user_profile_service.go +4 -4
  13. space/config/database_connection_config.go +4 -0
  14. space/controller/cv/cv_controller.go +267 -0
  15. space/main.go +9 -1
  16. space/middleware/authentication_middleware.go +7 -0
  17. space/models/database_orm_model.go +118 -33
  18. space/models/request_model.go +126 -51
  19. space/repositories/cv_repository.go +126 -0
  20. space/router/cv_route.go +26 -0
  21. space/router/router.go +11 -10
  22. space/router/server.go +4 -0
  23. space/services/cv_service.go +108 -0
  24. space/space/services/health_check_service.go +1 -2
  25. space/space/space/.gitignore +1 -0
  26. space/space/space/controller/health_check/health_check_controller.go +12 -8
  27. space/space/space/main.go +2 -2
  28. space/space/space/models/database_orm_model.go +207 -207
  29. space/space/space/models/exception_model.go +19 -13
  30. space/space/space/models/health_check_model.go +1 -3
  31. space/space/space/repositories/health_check_repository.go +12 -13
  32. space/space/space/response/api_response_v2.go +79 -0
  33. space/space/space/response/gorm.go +62 -0
  34. space/space/space/router/server.go +2 -2
  35. space/space/space/services/health_check_service.go +32 -0
  36. space/space/space/services/user_profile_service.go +115 -115
  37. space/space/space/space/Dockerfile +6 -0
  38. space/space/space/space/space/.github/workflows/main.yml +1 -0
  39. space/space/space/space/space/apperror/apperror.go +168 -0
  40. space/space/space/space/space/config/database_connection_config.go +80 -79
  41. space/space/space/space/space/config/tx.go +34 -0
  42. space/space/space/space/space/controller/health_check/health_check_controller.go +31 -0
  43. space/space/space/space/space/models/exception_model.go +17 -15
  44. space/space/space/space/space/models/health_check_model.go +10 -0
  45. space/space/space/space/space/repositories/health_check_repository.go +41 -0
  46. space/space/space/space/space/response/paging.go +58 -0
  47. space/space/space/space/space/response/response.go +152 -0
  48. space/space/space/space/space/router/health_check_route.go +5 -0
  49. space/space/space/space/space/router/router.go +12 -19
  50. space/space/space/space/space/router/server.go +32 -0
config/database_connection_config.go CHANGED
@@ -71,6 +71,9 @@ func AutoMigrateAll(db *gorm.DB) {
71
  &models.Answer{},
72
  &models.UserAnswer{},
73
  &models.PersonalityAndPreferenceCV{},
 
 
 
74
  )
75
 
76
  if err != nil {
 
71
  &models.Answer{},
72
  &models.UserAnswer{},
73
  &models.PersonalityAndPreferenceCV{},
74
+ &models.FamilyMemberCV{},
75
+ &models.PhysicalAndHealthCV{},
76
+ &models.WorshipAndReligiousUnderstandingCV{},
77
  )
78
 
79
  if err != nil {
controller/cv/cv_controller.go CHANGED
@@ -7,11 +7,27 @@ import (
7
  "api.qobiltu.id/services"
8
  "github.com/gin-gonic/gin"
9
  "net/http"
 
10
  )
11
 
12
  type CVController interface {
 
 
 
13
  SavePersonalityAndPreference(ctx *gin.Context)
14
  GetPersonalityAndPreference(ctx *gin.Context)
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
 
17
  type cvController struct {
@@ -24,6 +40,41 @@ func NewCVController(cvService services.CVService) CVController {
24
  }
25
  }
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  func (c *cvController) SavePersonalityAndPreference(ctx *gin.Context) {
28
  var req models.PersonalityAndPreferenceCVRequest
29
  if err := ctx.ShouldBindJSON(&req); err != nil {
@@ -55,3 +106,162 @@ func (c *cvController) GetPersonalityAndPreference(ctx *gin.Context) {
55
 
56
  response.HandleSuccess(ctx, http.StatusOK, "Get Personality and Preference success", res, nil)
57
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 {
 
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 {
 
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
+ }
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
+ }
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
+ }
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,49 +186,107 @@ type QuizResult struct {
184
  AverageScore float64 `gorm:"column:average_score" json:"average_score"`
185
  }
186
 
187
- type PersonalityAndPreferenceCV struct {
188
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
189
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
190
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
191
- PositiveTraits *string `gorm:"column:positive_traits" json:"positive_traits"` // sifat positif
192
- NegativeTraits *string `gorm:"column:negative_traits" json:"negative_traits"` // sifat negatif
193
- Hobbies *string `gorm:"column:hobbies" json:"hobbies"` // hobi
194
- LifeGoals *string `gorm:"column:life_goals" json:"life_goals"` // target hidup
195
- DailyActivities *string `gorm:"column:daily_activities" json:"daily_activities"` // kegiatan sehari-hari
196
- LeisureActivities *string `gorm:"column:leisure_activities" json:"leisure_activities"` // kegiatan waktu luang
197
- Likes *string `gorm:"column:likes" json:"likes"` // hal yang disukai
198
- Dislikes *string `gorm:"column:dislikes" json:"dislikes"` // hal yang tidak disukai
199
- StressHandling *string `gorm:"column:stress_handling" json:"stress_handling"` // cara mengatasi stres
200
- AngerTriggers *string `gorm:"column:anger_triggers" json:"anger_triggers"` // pemicu amarah
201
- FavoriteFoodAndDrinks *string `gorm:"column:favorite_food_and_drinks" json:"favorite_food_and_drinks"` // makanan dan minuman favorit
202
- CanCook *bool `gorm:"column:can_cook" json:"can_cook"` // bisa memasak
203
- TypesOfDishesCooked *string `gorm:"column:types_of_dishes_cooked" json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
204
- MonthlyExpenses *string `gorm:"column:monthly_expenses" json:"monthly_expenses"` // pengeluaran per bulan
205
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
206
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
207
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
  // Gorm table name settings
210
- func (Account) TableName() string { return "account" }
211
- func (AccountDetails) TableName() string { return "account_details" }
212
- func (EmailVerification) TableName() string { return "email_verifications" }
213
- func (ExternalAuth) TableName() string { return "extern_auth" }
214
- func (FCM) TableName() string { return "fcm" }
215
- func (ForgotPassword) TableName() string { return "forgot_password" }
216
- func (Academy) TableName() string { return "academy" }
217
- func (AcademyMaterial) TableName() string { return "academy_materials" }
218
- func (AcademyContent) TableName() string { return "academy_contents" }
219
- func (AcademyMaterialProgress) TableName() string { return "academy_materials_progress" }
220
- func (AcademyContentProgress) TableName() string { return "academy_contents_progress" }
221
- func (RegionProvince) TableName() string { return "region_provinces" }
222
- func (RegionCity) TableName() string { return "region_cities" }
223
- func (Answer) TableName() string { return "answers" }
224
- func (Question) TableName() string { return "questions" }
225
- func (Quiz) TableName() string { return "quizzes" }
226
- func (QuizAttempt) TableName() string { return "quiz_attempts" }
227
- func (UserAnswer) TableName() string { return "user_answers" }
228
- func (OptionCategory) TableName() string { return "option_categories" }
229
- func (OptionValues) TableName() string { return "option_values" }
230
- func (PersonalityAndPreferenceCV) TableName() string {
231
- return "personality_and_preference_cv"
 
 
 
232
  }
 
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
  }
models/request_model.go CHANGED
@@ -54,7 +54,7 @@ type AnswerQuizRequest struct {
54
 
55
  type (
56
  PersonalityAndPreferenceCVRequest struct {
57
- AccountID int64 `json:"account_id"`
58
  PositiveTraits *string `json:"positive_traits"` // sifat positif
59
  NegativeTraits *string `json:"negative_traits"` // sifat negatif
60
  Hobbies *string `json:"hobbies"` // hobi
@@ -71,24 +71,56 @@ type (
71
  MonthlyExpenses *string `json:"monthly_expenses"` // pengeluaran per bulan
72
  }
73
 
74
- PersonalityAndPreferenceCVResponse struct {
75
- ID int64 `json:"id"`
76
- AccountID int64 `json:"account_id"`
77
- PositiveTraits *string `json:"positive_traits"` // sifat positif
78
- NegativeTraits *string `json:"negative_traits"` // sifat negatif
79
- Hobbies *string `json:"hobbies"` // hobi
80
- LifeGoals *string `json:"life_goals"` // target hidup
81
- DailyActivities *string `json:"daily_activities"` // kegiatan sehari-hari
82
- LeisureActivities *string `json:"leisure_activities"` // kegiatan waktu luang
83
- Likes *string `json:"likes"` // hal yang disukai
84
- Dislikes *string `json:"dislikes"` // hal yang tidak disukai
85
- StressHandling *string `json:"stress_handling"` // cara mengatasi stres
86
- AngerTriggers *string `json:"anger_triggers"` // pemicu amarah
87
- FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
88
- CanCook *bool `json:"can_cook"` // bisa memasak
89
- TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
90
- MonthlyExpenses *string `json:"monthly_expenses"` // pengeluaran per bulan
91
- CreatedAt time.Time `json:"created_at"`
92
- UpdatedAt time.Time `json:"updated_at"`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
  )
 
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
 
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
  )
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
+ }
repositories/cv_repository.go CHANGED
@@ -1,37 +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
- SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error)
11
- GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  }
13
 
14
  type cvRepository struct {
15
- db *gorm.DB
16
  }
17
 
18
  func NewCVRepository(db *gorm.DB) CVRepository {
19
- return &cvRepository{
20
- db: db,
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
  func (r *cvRepository) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error) {
25
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
26
- return req, err
27
- }
28
- return req, nil
29
  }
30
 
31
  func (r *cvRepository) GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error) {
32
- var personalityAndPreference models.PersonalityAndPreferenceCV
33
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&personalityAndPreference).Error; err != nil {
34
- return nil, err
35
- }
36
- return &personalityAndPreference, nil
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
 
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
  }
router/cv_route.go CHANGED
@@ -3,9 +3,24 @@ package router
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("/personality-and-preference", s.cvController.SavePersonalityAndPreference)
9
- routerGroup.GET("/personality-and-preference", s.cvController.GetPersonalityAndPreference)
10
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  }
 
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
  }
services/cv_service.go CHANGED
@@ -1,108 +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
- SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCVResponse, error)
14
- GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCVResponse, error)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
 
17
  type cvService struct {
18
- cvRepository repositories.CVRepository
19
  }
20
 
21
  func NewCVService(cvRepository repositories.CVRepository) CVService {
22
- return &cvService{
23
- cvRepository: cvRepository,
24
- }
25
- }
26
-
27
- func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCVResponse, error) {
28
- // Ambil data lama jika ada
29
- personalityAndPreference, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, req.AccountID)
30
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
31
- return nil, response.HandleGormError(err, "Internal Server Error")
32
- }
33
-
34
- // Apply perubahan
35
- if personalityAndPreference == nil {
36
- personalityAndPreference = &models.PersonalityAndPreferenceCV{}
37
- }
38
-
39
- personalityAndPreference.AccountID = req.AccountID
40
- personalityAndPreference.PositiveTraits = req.PositiveTraits
41
- personalityAndPreference.NegativeTraits = req.NegativeTraits
42
- personalityAndPreference.Hobbies = req.Hobbies
43
- personalityAndPreference.LifeGoals = req.LifeGoals
44
- personalityAndPreference.DailyActivities = req.DailyActivities
45
- personalityAndPreference.LeisureActivities = req.LeisureActivities
46
- personalityAndPreference.Likes = req.Likes
47
- personalityAndPreference.Dislikes = req.Dislikes
48
- personalityAndPreference.StressHandling = req.StressHandling
49
- personalityAndPreference.AngerTriggers = req.AngerTriggers
50
- personalityAndPreference.FavoriteFoodAndDrinks = req.FavoriteFoodAndDrinks
51
- personalityAndPreference.CanCook = req.CanCook
52
- personalityAndPreference.TypesOfDishesCooked = req.TypesOfDishesCooked
53
- personalityAndPreference.MonthlyExpenses = req.MonthlyExpenses
54
-
55
- res, err := s.cvRepository.SavePersonalityAndPreference(ctx, personalityAndPreference)
56
- if err != nil {
57
- return nil, response.HandleGormError(err, "Internal Server Error")
58
- }
59
-
60
- return &models.PersonalityAndPreferenceCVResponse{
61
- ID: res.ID,
62
- AccountID: res.AccountID,
63
- PositiveTraits: res.PositiveTraits,
64
- NegativeTraits: res.NegativeTraits,
65
- Hobbies: res.Hobbies,
66
- LifeGoals: res.LifeGoals,
67
- DailyActivities: res.DailyActivities,
68
- LeisureActivities: res.LeisureActivities,
69
- Likes: res.Likes,
70
- Dislikes: res.Dislikes,
71
- StressHandling: res.StressHandling,
72
- AngerTriggers: res.AngerTriggers,
73
- FavoriteFoodAndDrinks: res.FavoriteFoodAndDrinks,
74
- CanCook: res.CanCook,
75
- TypesOfDishesCooked: res.TypesOfDishesCooked,
76
- MonthlyExpenses: res.MonthlyExpenses,
77
- CreatedAt: res.CreatedAt,
78
- UpdatedAt: res.UpdatedAt,
79
- }, nil
80
- }
81
-
82
- func (s *cvService) GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCVResponse, error) {
83
- res, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, id)
84
- if err != nil {
85
- return nil, response.HandleGormError(err, "Internal Server Error")
86
- }
87
-
88
- return &models.PersonalityAndPreferenceCVResponse{
89
- ID: res.ID,
90
- AccountID: res.AccountID,
91
- PositiveTraits: res.PositiveTraits,
92
- NegativeTraits: res.NegativeTraits,
93
- Hobbies: res.Hobbies,
94
- LifeGoals: res.LifeGoals,
95
- DailyActivities: res.DailyActivities,
96
- LeisureActivities: res.LeisureActivities,
97
- Likes: res.Likes,
98
- Dislikes: res.Dislikes,
99
- StressHandling: res.StressHandling,
100
- AngerTriggers: res.AngerTriggers,
101
- FavoriteFoodAndDrinks: res.FavoriteFoodAndDrinks,
102
- CanCook: res.CanCook,
103
- TypesOfDishesCooked: res.TypesOfDishesCooked,
104
- MonthlyExpenses: res.MonthlyExpenses,
105
- CreatedAt: res.CreatedAt,
106
- UpdatedAt: res.UpdatedAt,
107
- }, nil
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  }
 
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
  }
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
+ }
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/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/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/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/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/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/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/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/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/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/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/services/cv_service.go ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCVResponse, error)
14
+ GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCVResponse, error)
15
+ }
16
+
17
+ type cvService struct {
18
+ cvRepository repositories.CVRepository
19
+ }
20
+
21
+ func NewCVService(cvRepository repositories.CVRepository) CVService {
22
+ return &cvService{
23
+ cvRepository: cvRepository,
24
+ }
25
+ }
26
+
27
+ func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCVResponse, error) {
28
+ // Ambil data lama jika ada
29
+ personalityAndPreference, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, req.AccountID)
30
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
31
+ return nil, response.HandleGormError(err, "Internal Server Error")
32
+ }
33
+
34
+ // Apply perubahan
35
+ if personalityAndPreference == nil {
36
+ personalityAndPreference = &models.PersonalityAndPreferenceCV{}
37
+ }
38
+
39
+ personalityAndPreference.AccountID = req.AccountID
40
+ personalityAndPreference.PositiveTraits = req.PositiveTraits
41
+ personalityAndPreference.NegativeTraits = req.NegativeTraits
42
+ personalityAndPreference.Hobbies = req.Hobbies
43
+ personalityAndPreference.LifeGoals = req.LifeGoals
44
+ personalityAndPreference.DailyActivities = req.DailyActivities
45
+ personalityAndPreference.LeisureActivities = req.LeisureActivities
46
+ personalityAndPreference.Likes = req.Likes
47
+ personalityAndPreference.Dislikes = req.Dislikes
48
+ personalityAndPreference.StressHandling = req.StressHandling
49
+ personalityAndPreference.AngerTriggers = req.AngerTriggers
50
+ personalityAndPreference.FavoriteFoodAndDrinks = req.FavoriteFoodAndDrinks
51
+ personalityAndPreference.CanCook = req.CanCook
52
+ personalityAndPreference.TypesOfDishesCooked = req.TypesOfDishesCooked
53
+ personalityAndPreference.MonthlyExpenses = req.MonthlyExpenses
54
+
55
+ res, err := s.cvRepository.SavePersonalityAndPreference(ctx, personalityAndPreference)
56
+ if err != nil {
57
+ return nil, response.HandleGormError(err, "Internal Server Error")
58
+ }
59
+
60
+ return &models.PersonalityAndPreferenceCVResponse{
61
+ ID: res.ID,
62
+ AccountID: res.AccountID,
63
+ PositiveTraits: res.PositiveTraits,
64
+ NegativeTraits: res.NegativeTraits,
65
+ Hobbies: res.Hobbies,
66
+ LifeGoals: res.LifeGoals,
67
+ DailyActivities: res.DailyActivities,
68
+ LeisureActivities: res.LeisureActivities,
69
+ Likes: res.Likes,
70
+ Dislikes: res.Dislikes,
71
+ StressHandling: res.StressHandling,
72
+ AngerTriggers: res.AngerTriggers,
73
+ FavoriteFoodAndDrinks: res.FavoriteFoodAndDrinks,
74
+ CanCook: res.CanCook,
75
+ TypesOfDishesCooked: res.TypesOfDishesCooked,
76
+ MonthlyExpenses: res.MonthlyExpenses,
77
+ CreatedAt: res.CreatedAt,
78
+ UpdatedAt: res.UpdatedAt,
79
+ }, nil
80
+ }
81
+
82
+ func (s *cvService) GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCVResponse, error) {
83
+ res, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, id)
84
+ if err != nil {
85
+ return nil, response.HandleGormError(err, "Internal Server Error")
86
+ }
87
+
88
+ return &models.PersonalityAndPreferenceCVResponse{
89
+ ID: res.ID,
90
+ AccountID: res.AccountID,
91
+ PositiveTraits: res.PositiveTraits,
92
+ NegativeTraits: res.NegativeTraits,
93
+ Hobbies: res.Hobbies,
94
+ LifeGoals: res.LifeGoals,
95
+ DailyActivities: res.DailyActivities,
96
+ LeisureActivities: res.LeisureActivities,
97
+ Likes: res.Likes,
98
+ Dislikes: res.Dislikes,
99
+ StressHandling: res.StressHandling,
100
+ AngerTriggers: res.AngerTriggers,
101
+ FavoriteFoodAndDrinks: res.FavoriteFoodAndDrinks,
102
+ CanCook: res.CanCook,
103
+ TypesOfDishesCooked: res.TypesOfDishesCooked,
104
+ MonthlyExpenses: res.MonthlyExpenses,
105
+ CreatedAt: res.CreatedAt,
106
+ UpdatedAt: res.UpdatedAt,
107
+ }, nil
108
+ }
space/space/services/health_check_service.go CHANGED
@@ -5,7 +5,6 @@ import (
5
  "api.qobiltu.id/repositories"
6
  "api.qobiltu.id/response"
7
  "context"
8
- "gorm.io/gorm"
9
  )
10
 
11
  type HealthCheckService interface {
@@ -25,7 +24,7 @@ func NewHealthCheckService(healthCheckRepository repositories.HealthCheckReposit
25
  func (s *healthCheckService) Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error) {
26
  res, err := s.healthCheckRepository.Check(ctx, req)
27
  if err != nil {
28
- return nil, response.HandleGormError(gorm.ErrDuplicatedKey, "Internal Server Error")
29
  }
30
 
31
  return res, nil
 
5
  "api.qobiltu.id/repositories"
6
  "api.qobiltu.id/response"
7
  "context"
 
8
  )
9
 
10
  type HealthCheckService interface {
 
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
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/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/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/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/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/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/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/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/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/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/services/health_check_service.go ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "api.qobiltu.id/models"
5
+ "api.qobiltu.id/repositories"
6
+ "api.qobiltu.id/response"
7
+ "context"
8
+ "gorm.io/gorm"
9
+ )
10
+
11
+ type HealthCheckService interface {
12
+ Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error)
13
+ }
14
+
15
+ type healthCheckService struct {
16
+ healthCheckRepository repositories.HealthCheckRepository
17
+ }
18
+
19
+ func NewHealthCheckService(healthCheckRepository repositories.HealthCheckRepository) HealthCheckService {
20
+ return &healthCheckService{
21
+ healthCheckRepository: healthCheckRepository,
22
+ }
23
+ }
24
+
25
+ func (s *healthCheckService) Check(ctx context.Context, req *models.HealthCheckRequest) (*models.HealthCheckResponse, error) {
26
+ res, err := s.healthCheckRepository.Check(ctx, req)
27
+ if err != nil {
28
+ return nil, response.HandleGormError(gorm.ErrDuplicatedKey, "Internal Server Error")
29
+ }
30
+
31
+ return res, nil
32
+ }
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/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
 
space/space/space/space/space/.github/workflows/main.yml CHANGED
@@ -25,4 +25,5 @@ jobs:
25
  git pull origin main
26
  docker-compose down -v
27
  docker-compose up --build -d
 
28
  EOF
 
25
  git pull origin main
26
  docker-compose down -v
27
  docker-compose up --build -d
28
+ docker image prune -f
29
  EOF
space/space/space/space/space/apperror/apperror.go ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package apperror
2
+
3
+ import (
4
+ "api.qobiltu.id/validation"
5
+ "fmt"
6
+ )
7
+
8
+ // AppError is a custom error type for the application.
9
+ type AppError struct {
10
+ Code string // Optional: Error code for programmatic handling
11
+ Message string // Human-readable error message
12
+ Err error // Underlying error, if any
13
+ Details map[string]any // Optional: Additional details about the error
14
+ }
15
+
16
+ func (e AppError) Error() string {
17
+ if e.Code != "" {
18
+ return fmt.Sprintf("[%s] %s", e.Code, e.Message)
19
+ }
20
+ return e.Message
21
+ }
22
+
23
+ // NewAppError creates a new AppError.
24
+ func NewAppError(code, message string, err error, details map[string]any) error {
25
+ return &AppError{
26
+ Code: code,
27
+ Message: message,
28
+ Err: err,
29
+ Details: details,
30
+ }
31
+ }
32
+
33
+ // ValidationError represents errors related to data validation.
34
+ type ValidationError struct {
35
+ Errors []validation.ErrorMessage // Structure to hold field and message of validation errors
36
+ }
37
+
38
+ func (e ValidationError) Error() string {
39
+ return fmt.Sprintf("validation failed: %+v", e.Errors)
40
+ }
41
+
42
+ // NewValidationError creates a new ValidationError.
43
+ func NewValidationError(message string, errors []validation.ErrorMessage) error {
44
+ return &AppError{
45
+ Code: "VALIDATION_ERROR",
46
+ Message: message,
47
+ Err: ValidationError{Errors: errors},
48
+ }
49
+ }
50
+
51
+ // InternalError represents unexpected errors within the application.
52
+ type InternalError struct {
53
+ Message string
54
+ Err error
55
+ }
56
+
57
+ func (e InternalError) Error() string {
58
+ return fmt.Sprintf("internal server error: %v", e.Err)
59
+ }
60
+
61
+ // NewInternalError creates a new InternalError wrapped in AppError.
62
+ func NewInternalError(message string, err error) error {
63
+ return &AppError{
64
+ Code: "INTERNAL_ERROR",
65
+ Message: message,
66
+ Err: InternalError{Message: message, Err: err},
67
+ }
68
+ }
69
+
70
+ // ConflictError represents errors due to a conflict with the current state.
71
+ type ConflictError struct {
72
+ Message string
73
+ Err error
74
+ }
75
+
76
+ func (e ConflictError) Error() string {
77
+ return fmt.Sprintf("conflict: %s", e.Err)
78
+ }
79
+
80
+ // NewConflictError creates a new ConflictError wrapped in AppError.
81
+ func NewConflictError(message string, err error) error {
82
+ return &AppError{
83
+ Code: "CONFLICT_ERROR",
84
+ Message: message,
85
+ Err: ConflictError{Message: message, Err: err},
86
+ }
87
+ }
88
+
89
+ // NotFoundError represents errors when a resource is not found.
90
+ type NotFoundError struct {
91
+ Message string
92
+ Resource string
93
+ ID any
94
+ }
95
+
96
+ func (e NotFoundError) Error() string {
97
+ return fmt.Sprintf("%s with ID '%v' not found", e.Resource, e.ID)
98
+ }
99
+
100
+ // NewNotFoundError creates a new NotFoundError wrapped in AppError.
101
+ func NewNotFoundError(resource string, id any) error {
102
+ message := fmt.Sprintf("%s not found", resource)
103
+ return &AppError{
104
+ Code: "NOT_FOUND_ERROR",
105
+ Message: fmt.Sprintf("%s not found", resource),
106
+ Err: NotFoundError{Message: message, Resource: resource, ID: id},
107
+ Details: map[string]any{
108
+ "resource": resource,
109
+ "id": id,
110
+ },
111
+ }
112
+ }
113
+
114
+ // UnauthorizedError represents errors when access is denied due to lack of credentials.
115
+ type UnauthorizedError struct {
116
+ Message string
117
+ Err error
118
+ }
119
+
120
+ func (e UnauthorizedError) Error() string {
121
+ return fmt.Sprintf("unauthorized: %s", e.Err)
122
+ }
123
+
124
+ // NewUnauthorizedError creates a new UnauthorizedError wrapped in AppError.
125
+ func NewUnauthorizedError(message string, err error) error {
126
+ return &AppError{
127
+ Code: "UNAUTHORIZED_ERROR",
128
+ Message: message,
129
+ Err: UnauthorizedError{Message: message, Err: err},
130
+ }
131
+ }
132
+
133
+ // ForbiddenError represents errors when access is denied even with valid credentials.
134
+ type ForbiddenError struct {
135
+ Message string
136
+ Err error
137
+ }
138
+
139
+ func (e ForbiddenError) Error() string {
140
+ return fmt.Sprintf("forbidden: %s", e.Err)
141
+ }
142
+
143
+ // NewForbiddenError creates a new ForbiddenError wrapped in AppError.
144
+ func NewForbiddenError(message string, err error) error {
145
+ return &AppError{
146
+ Code: "FORBIDDEN_ERROR",
147
+ Message: message,
148
+ Err: ForbiddenError{Message: message, Err: err},
149
+ }
150
+ }
151
+
152
+ // BadRequestError represents errors due to an invalid request.
153
+ type BadRequestError struct {
154
+ Message string
155
+ Err error
156
+ }
157
+
158
+ func (e BadRequestError) Error() string {
159
+ return fmt.Sprintf("bad request: %s", e.Err)
160
+ }
161
+
162
+ func NewBadRequestError(message string, err error) error {
163
+ return &AppError{
164
+ Code: "BAD_REQUEST_ERROR",
165
+ Message: message,
166
+ Err: BadRequestError{Message: message, Err: err},
167
+ }
168
+ }
space/space/space/space/space/config/database_connection_config.go CHANGED
@@ -1,79 +1,80 @@
1
- package config
2
-
3
- import (
4
- "fmt"
5
- "log"
6
- "os"
7
-
8
- "gorm.io/driver/postgres"
9
- "gorm.io/gorm"
10
- "gorm.io/gorm/logger"
11
-
12
- "api.qobiltu.id/models"
13
- "github.com/joho/godotenv"
14
- )
15
-
16
- var DB *gorm.DB
17
- var err error
18
- var Salt string
19
-
20
- func init() {
21
- godotenv.Load()
22
- if err != nil {
23
- fmt.Println("Gagal membaca file .env")
24
- return
25
- }
26
- os.Setenv("TZ", "Asia/Jakarta")
27
- dbHost := os.Getenv("DB_HOST")
28
- dbPort := os.Getenv("DB_PORT")
29
- dbUser := os.Getenv("DB_USER")
30
- dbPassword := os.Getenv("DB_PASSWORD")
31
- dbName := os.Getenv("DB_NAME")
32
- Salt := os.Getenv("SALT")
33
- dsn := "host=" + dbHost + " user=" + dbUser + " password=" + dbPassword + " dbname=" + dbName + " port=" + dbPort + " sslmode=disable TimeZone=Asia/Jakarta"
34
- DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{TranslateError: true})
35
- if err != nil {
36
- panic(err)
37
- }
38
- if Salt == "" {
39
- Salt = "D3f4u|t"
40
- }
41
-
42
- // Call AutoMigrateAll to perform auto-migration
43
- AutoMigrateAll(DB)
44
- }
45
-
46
- func AutoMigrateAll(db *gorm.DB) {
47
- // Enable logger to see SQL logs
48
- db.Logger.LogMode(logger.Info)
49
-
50
- // Auto-migrate all models
51
- err := db.AutoMigrate(
52
- &models.Account{},
53
- &models.AccountDetails{},
54
- &models.EmailVerification{},
55
- &models.ExternalAuth{},
56
- &models.FCM{},
57
- &models.ForgotPassword{},
58
- &models.Academy{},
59
- &models.AcademyMaterial{},
60
- &models.AcademyContent{},
61
- &models.AcademyMaterialProgress{},
62
- &models.AcademyContentProgress{},
63
- &models.RegionCity{},
64
- &models.RegionProvince{},
65
- &models.OptionCategory{},
66
- &models.OptionValues{},
67
- &models.Quiz{},
68
- &models.QuizAttempt{},
69
- &models.Question{},
70
- &models.Answer{},
71
- &models.UserAnswer{},
72
- )
73
-
74
- if err != nil {
75
- log.Fatal(err)
76
- }
77
-
78
- fmt.Println("Migration completed successfully.")
79
- }
 
 
1
+ package config
2
+
3
+ import (
4
+ "fmt"
5
+ "log"
6
+ "log/slog"
7
+ "os"
8
+
9
+ "gorm.io/driver/postgres"
10
+ "gorm.io/gorm"
11
+ "gorm.io/gorm/logger"
12
+
13
+ "api.qobiltu.id/models"
14
+ "github.com/joho/godotenv"
15
+ )
16
+
17
+ var DB *gorm.DB
18
+ var err error
19
+ var Salt string
20
+
21
+ func init() {
22
+ godotenv.Load()
23
+ if err != nil {
24
+ fmt.Println("Gagal membaca file .env")
25
+ return
26
+ }
27
+ os.Setenv("TZ", "Asia/Jakarta")
28
+ dbHost := os.Getenv("DB_HOST")
29
+ dbPort := os.Getenv("DB_PORT")
30
+ dbUser := os.Getenv("DB_USER")
31
+ dbPassword := os.Getenv("DB_PASSWORD")
32
+ dbName := os.Getenv("DB_NAME")
33
+ Salt := os.Getenv("SALT")
34
+ dsn := "host=" + dbHost + " user=" + dbUser + " password=" + dbPassword + " dbname=" + dbName + " port=" + dbPort + " sslmode=disable TimeZone=Asia/Jakarta"
35
+ DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{TranslateError: true})
36
+ if err != nil {
37
+ panic(err)
38
+ }
39
+ if Salt == "" {
40
+ Salt = "D3f4u|t"
41
+ }
42
+
43
+ // Call AutoMigrateAll to perform auto-migration
44
+ AutoMigrateAll(DB)
45
+ }
46
+
47
+ func AutoMigrateAll(db *gorm.DB) {
48
+ // Enable logger to see SQL logs
49
+ db.Logger.LogMode(logger.Info)
50
+
51
+ // Auto-migrate all models
52
+ err := db.AutoMigrate(
53
+ &models.Account{},
54
+ &models.AccountDetails{},
55
+ &models.EmailVerification{},
56
+ &models.ExternalAuth{},
57
+ &models.FCM{},
58
+ &models.ForgotPassword{},
59
+ &models.Academy{},
60
+ &models.AcademyMaterial{},
61
+ &models.AcademyContent{},
62
+ &models.AcademyMaterialProgress{},
63
+ &models.AcademyContentProgress{},
64
+ &models.RegionCity{},
65
+ &models.RegionProvince{},
66
+ &models.OptionCategory{},
67
+ &models.OptionValues{},
68
+ &models.Quiz{},
69
+ &models.QuizAttempt{},
70
+ &models.Question{},
71
+ &models.Answer{},
72
+ &models.UserAnswer{},
73
+ )
74
+
75
+ if err != nil {
76
+ log.Fatal(err)
77
+ }
78
+
79
+ slog.Info("Auto-migration completed successfully")
80
+ }
space/space/space/space/space/config/tx.go ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package config
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "gorm.io/gorm"
7
+ )
8
+
9
+ func RunTx(ctx context.Context, db *gorm.DB, fn func(tx *gorm.DB) error) error {
10
+ tx := db.WithContext(ctx).Begin()
11
+ if tx.Error != nil {
12
+ return fmt.Errorf("failed to begin transaction: %w", tx.Error)
13
+ }
14
+
15
+ defer func() {
16
+ if p := recover(); p != nil {
17
+ _ = tx.Rollback()
18
+ panic(p) // Re-throw panic setelah rollback
19
+ }
20
+ }()
21
+
22
+ if err := fn(tx); err != nil {
23
+ if rbErr := tx.Rollback().Error; rbErr != nil {
24
+ return fmt.Errorf("transaction error: %v, rollback error: %w", err, rbErr)
25
+ }
26
+ return err
27
+ }
28
+
29
+ if err := tx.Commit().Error; err != nil {
30
+ return fmt.Errorf("failed to commit transaction: %w", err)
31
+ }
32
+
33
+ return nil
34
+ }
space/space/space/space/space/controller/health_check/health_check_controller.go ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package health_check_controller
2
+
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
+ }
space/space/space/space/space/models/exception_model.go CHANGED
@@ -1,15 +1,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
- Message string `json:"message,omitempty"`
15
- }
 
 
 
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
+ }
space/space/space/space/space/models/health_check_model.go ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
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"`
9
+ RedisStatus string `json:"redis_status"`
10
+ }
space/space/space/space/space/repositories/health_check_repository.go ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "api.qobiltu.id/config"
5
+ "api.qobiltu.id/models"
6
+ "context"
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
+
36
+ if err != nil {
37
+ return nil, err
38
+ }
39
+
40
+ return res, nil
41
+ }
space/space/space/space/space/response/paging.go ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package response
2
+
3
+ import (
4
+ "errors"
5
+ "fmt"
6
+ )
7
+
8
+ type PagingInfo struct {
9
+ HasPreviousPage bool `json:"has_previous_page"`
10
+ HasNextPage bool `json:"has_next_page"`
11
+ CurrentPage int `json:"current_page"`
12
+ PerPage int `json:"per_page"`
13
+ TotalData int `json:"total_data"`
14
+ LastPage int `json:"last_page"`
15
+ From int `json:"from"`
16
+ To int `json:"to"`
17
+ TotalDataInCurrentPage int `json:"total_data_in_current_page"`
18
+ Label string `json:"label"`
19
+ }
20
+
21
+ var ErrPageInfo = errors.New("per_page harus lebih besar dari 0 dan offset tidak boleh negatif")
22
+
23
+ func NewPagingInfo(currentPage, perPage, offset, totalData int) (*PagingInfo, error) {
24
+ if perPage <= 0 || offset < 0 {
25
+ return nil, ErrPageInfo
26
+ }
27
+
28
+ lastPage := totalData / perPage
29
+ if totalData%perPage != 0 {
30
+ lastPage++
31
+ }
32
+
33
+ to := min(offset+perPage, totalData)
34
+ from := int(0)
35
+ if to > offset {
36
+ from = offset + 1
37
+ }
38
+
39
+ if currentPage > lastPage {
40
+ currentPage = lastPage
41
+ }
42
+
43
+ totalDataInCurrentPage := to - offset
44
+ label := fmt.Sprintf("Menampilkan %d sampai %d dari %d data", from, to, totalData)
45
+
46
+ return &PagingInfo{
47
+ HasPreviousPage: currentPage > 1,
48
+ HasNextPage: currentPage < lastPage,
49
+ CurrentPage: currentPage,
50
+ PerPage: perPage,
51
+ TotalData: totalData,
52
+ LastPage: lastPage,
53
+ From: from,
54
+ To: to,
55
+ TotalDataInCurrentPage: totalDataInCurrentPage,
56
+ Label: label,
57
+ }, nil
58
+ }
space/space/space/space/space/response/response.go ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package response
2
+
3
+ import (
4
+ errors "api.qobiltu.id/apperror"
5
+ "api.qobiltu.id/models"
6
+ goErrors "errors"
7
+ "fmt"
8
+ "github.com/gin-gonic/gin"
9
+ "github.com/go-playground/validator/v10"
10
+ "net/http"
11
+ )
12
+
13
+ type ErrorResponse struct {
14
+ Details string `json:"details"`
15
+ ValidationErrors []ValidationError `json:"validation_errors,omitempty"`
16
+ models.Exception
17
+ }
18
+
19
+ type API struct {
20
+ Status string `json:"status"`
21
+ Message string `json:"message,omitempty"`
22
+ Data any `json:"data,omitempty"`
23
+ MetaData any `json:"meta_data"`
24
+ Error *ErrorResponse `json:"errors,omitempty"`
25
+ }
26
+
27
+ type List struct {
28
+ List any `json:"list"`
29
+ }
30
+
31
+ type ValidationError struct {
32
+ Field string `json:"field"`
33
+ Message string `json:"message"`
34
+ }
35
+
36
+ func HandleError(c *gin.Context, err error) {
37
+
38
+ apiResponse := API{
39
+ Status: "error",
40
+ Message: "An error occurred, " + err.Error(),
41
+ Error: &ErrorResponse{
42
+ Exception: models.Exception{Message: ""},
43
+ },
44
+ MetaData: struct{}{},
45
+ }
46
+
47
+ var appErr *errors.AppError
48
+
49
+ if goErrors.As(err, &appErr) {
50
+ apiResponse.Error.Details = fmt.Sprintf("%v", appErr.Err)
51
+
52
+ switch specificErr := appErr.Err.(type) {
53
+ case errors.ValidationError:
54
+ validationError := make([]ValidationError, len(specificErr.Errors))
55
+ apiResponse.Message = "Validation Error"
56
+ apiResponse.Error.ValidationErrors = validationError
57
+ for i, ve := range specificErr.Errors {
58
+ apiResponse.Error.ValidationErrors[i] = ValidationError{
59
+ Field: ve.Field,
60
+ Message: ve.Message,
61
+ }
62
+ }
63
+ c.JSON(http.StatusBadRequest, apiResponse)
64
+ return
65
+ case errors.ConflictError:
66
+ apiResponse.Message = specificErr.Message
67
+ apiResponse.Error.Details = specificErr.Error()
68
+ apiResponse.Error.Exception.DataDuplicate = true
69
+ c.JSON(http.StatusConflict, apiResponse)
70
+ return
71
+ case errors.InternalError:
72
+ apiResponse.Message = specificErr.Message
73
+ apiResponse.Error.Details = specificErr.Error()
74
+ apiResponse.Error.Exception.InternalServerError = true
75
+ c.JSON(http.StatusInternalServerError, apiResponse)
76
+ return
77
+ case errors.NotFoundError:
78
+ apiResponse.Message = specificErr.Message
79
+ apiResponse.Error.Details = specificErr.Error()
80
+ apiResponse.Error.Exception.DataNotFound = true
81
+ c.JSON(http.StatusNotFound, apiResponse)
82
+ return
83
+ case errors.UnauthorizedError:
84
+ apiResponse.Message = specificErr.Message
85
+ apiResponse.Error.Details = specificErr.Error()
86
+ apiResponse.Error.Exception.Unauthorized = true
87
+ c.JSON(http.StatusUnauthorized, apiResponse)
88
+ return
89
+ case errors.ForbiddenError:
90
+ apiResponse.Message = specificErr.Message
91
+ apiResponse.Error.Details = specificErr.Error()
92
+ apiResponse.Error.Exception.Forbidden = true
93
+ c.JSON(http.StatusForbidden, apiResponse)
94
+ return
95
+ case errors.BadRequestError:
96
+ apiResponse.Message = specificErr.Message
97
+ apiResponse.Error.Details = specificErr.Error()
98
+ apiResponse.Error.Exception.BadRequest = true
99
+ c.JSON(http.StatusBadRequest, apiResponse)
100
+ return
101
+ default:
102
+ apiResponse.Error.Details = "An unexpected error occurred."
103
+ apiResponse.Error.Exception.InternalServerError = true
104
+ c.JSON(http.StatusInternalServerError, apiResponse)
105
+ return
106
+ }
107
+ } else if validationErrors, ok := err.(validator.ValidationErrors); ok {
108
+ apiResponse.Error.Details = "Validation failed for the request."
109
+ apiResponse.Error.Exception.ValidationError = true
110
+ apiResponse.Error.ValidationErrors = make([]ValidationError, len(validationErrors))
111
+ for i, fe := range validationErrors {
112
+ apiResponse.Error.ValidationErrors[i] = ValidationError{
113
+ Field: fe.Field(),
114
+ Message: fe.Translate(nil), // adjust if you use translator
115
+ }
116
+ }
117
+ c.JSON(http.StatusBadRequest, apiResponse)
118
+ return
119
+ }
120
+
121
+ c.JSON(http.StatusInternalServerError, apiResponse)
122
+ apiResponse.Error.Details = err.Error()
123
+ }
124
+
125
+ func HandleSuccess(c *gin.Context, message string, statusCode int, data any, metaData any) {
126
+ apiResponse := API{
127
+ Status: "success",
128
+ Message: message,
129
+ Data: data,
130
+ MetaData: metaData,
131
+ }
132
+
133
+ if metaData == nil {
134
+ apiResponse.MetaData = struct{}{}
135
+ }
136
+
137
+ c.JSON(statusCode, apiResponse)
138
+ }
139
+
140
+ func HandleSuccessWithPaging(c *gin.Context, message string, content any, pagingInfo *PagingInfo) {
141
+
142
+ apiResponse := API{
143
+ Message: message,
144
+ Status: "success",
145
+ Data: List{
146
+ List: content,
147
+ },
148
+ MetaData: pagingInfo,
149
+ }
150
+
151
+ c.JSON(http.StatusOK, apiResponse)
152
+ }
space/space/space/space/space/router/health_check_route.go ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ package router
2
+
3
+ func (s *Server) HealthCheckRoute() {
4
+ s.router.GET("/api/v1/health-check", s.healthCheckController.Check)
5
+ }
space/space/space/space/space/router/router.go CHANGED
@@ -1,27 +1,20 @@
1
  package router
2
 
3
  import (
4
- "log"
5
-
6
- "api.qobiltu.id/config"
7
- "api.qobiltu.id/controller"
8
- "github.com/gin-gonic/gin"
9
  )
10
 
11
- func StartService() {
12
- router := gin.Default()
13
- router.Use(gin.Recovery())
14
 
15
- router.GET("/", controller.HomeController)
16
- AuthRoute(router)
17
- UserRoute(router)
18
- EmailRoute(router)
19
- OptionsRoute(router)
20
- AcademyRoute(router)
21
- QuizRoute(router)
22
 
23
- err := router.Run(config.TCP_ADDRESS)
24
- if err != nil {
25
- log.Fatalf("Failed to run server: %v", err)
26
- }
27
  }
 
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
  }
space/space/space/space/space/router/server.go ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import (
4
+ "api.qobiltu.id/controller/health_check"
5
+ "github.com/gin-gonic/gin"
6
+ )
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()
18
+ router.Use(gin.Recovery())
19
+
20
+ server := &Server{
21
+ healthCheckController: healthCheckController,
22
+ router: router,
23
+ }
24
+
25
+ server.setupRoutes()
26
+
27
+ return server, nil
28
+ }
29
+
30
+ func (s *Server) Start(address string) error {
31
+ return s.router.Run(address)
32
+ }