fix: auth and permission chmod and regist flow

This commit is contained in:
pahmiudahgede 2025-06-11 06:03:08 +07:00
parent e06b6033b5
commit c26eee0ab9
54 changed files with 1385 additions and 1755 deletions

View File

@ -1,66 +0,0 @@
package dto
import (
"strings"
)
type RequestAboutDTO struct {
Title string `json:"title"`
CoverImage string `json:"cover_image"`
}
func (r *RequestAboutDTO) ValidateAbout() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Title) == "" {
errors["title"] = append(errors["title"], "Title is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type ResponseAboutDTO struct {
ID string `json:"id"`
Title string `json:"title"`
CoverImage string `json:"cover_image"`
AboutDetail *[]ResponseAboutDetailDTO `json:"about_detail"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
type RequestAboutDetailDTO struct {
AboutId string `json:"about_id"`
ImageDetail string `json:"image_detail"`
Description string `json:"description"`
}
func (r *RequestAboutDetailDTO) ValidateAboutDetail() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.AboutId) == "" {
errors["about_id"] = append(errors["about_id"], "about_id is required")
}
if strings.TrimSpace(r.Description) == "" {
errors["description"] = append(errors["description"], "Description is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type ResponseAboutDetailDTO struct {
ID string `json:"id"`
AboutID string `json:"about_id"`
ImageDetail string `json:"image_detail"`
Description string `json:"description"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}

View File

@ -1,73 +0,0 @@
package dto
import "strings"
type AddressResponseDTO struct {
UserID string `json:"user_id,omitempty"`
ID string `json:"address_id,omitempty"`
Province string `json:"province,omitempty"`
Regency string `json:"regency,omitempty"`
District string `json:"district,omitempty"`
Village string `json:"village,omitempty"`
PostalCode string `json:"postalCode,omitempty"`
Detail string `json:"detail,omitempty"`
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
CreatedAt string `json:"createdAt,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
}
type CreateAddressDTO struct {
Province string `json:"province_id"`
Regency string `json:"regency_id"`
District string `json:"district_id"`
Village string `json:"village_id"`
PostalCode string `json:"postalCode"`
Detail string `json:"detail"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
func (r *CreateAddressDTO) ValidateAddress() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Province) == "" {
errors["province_id"] = append(errors["province_id"], "Province ID is required")
}
if strings.TrimSpace(r.Regency) == "" {
errors["regency_id"] = append(errors["regency_id"], "Regency ID is required")
}
if strings.TrimSpace(r.District) == "" {
errors["district_id"] = append(errors["district_id"], "District ID is required")
}
if strings.TrimSpace(r.Village) == "" {
errors["village_id"] = append(errors["village_id"], "Village ID is required")
}
if strings.TrimSpace(r.PostalCode) == "" {
errors["postalCode"] = append(errors["postalCode"], "PostalCode is required")
} else if len(r.PostalCode) < 5 {
errors["postalCode"] = append(errors["postalCode"], "PostalCode must be at least 5 characters")
}
if strings.TrimSpace(r.Detail) == "" {
errors["detail"] = append(errors["detail"], "Detail address is required")
}
if r.Latitude == 0 {
errors["latitude"] = append(errors["latitude"], "Latitude is required")
}
if r.Longitude == 0 {
errors["longitude"] = append(errors["longitude"], "Longitude is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,48 +0,0 @@
package dto
import (
"strings"
)
type ArticleResponseDTO struct {
ID string `json:"article_id"`
Title string `json:"title"`
CoverImage string `json:"coverImage"`
Author string `json:"author"`
Heading string `json:"heading"`
Content string `json:"content"`
PublishedAt string `json:"publishedAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestArticleDTO struct {
Title string `json:"title"`
CoverImage string `json:"coverImage"`
Author string `json:"author"`
Heading string `json:"heading"`
Content string `json:"content"`
}
func (r *RequestArticleDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Title) == "" {
errors["title"] = append(errors["title"], "Title is required")
}
if strings.TrimSpace(r.Author) == "" {
errors["author"] = append(errors["author"], "Author is required")
}
if strings.TrimSpace(r.Heading) == "" {
errors["heading"] = append(errors["heading"], "Heading is required")
}
if strings.TrimSpace(r.Content) == "" {
errors["content"] = append(errors["content"], "Content is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,130 +0,0 @@
package dto
import (
"regexp"
"strings"
)
type LoginAdminRequest struct {
Deviceid string `json:"device_id"`
Email string `json:"email"`
Password string `json:"password"`
}
type LoginResponse struct {
UserID string `json:"user_id"`
Role string `json:"role"`
Token string `json:"token"`
}
type RegisterAdminRequest struct {
Name string `json:"name"`
Gender string `json:"gender"`
Dateofbirth string `json:"dateofbirth"`
Placeofbirth string `json:"placeofbirth"`
Phone string `json:"phone"`
Email string `json:"email"`
Password string `json:"password"`
PasswordConfirm string `json:"password_confirm"`
}
type UserAdminDataResponse struct {
UserID string `json:"user_id"`
Name string `json:"name"`
Gender string `json:"gender"`
Dateofbirth string `json:"dateofbirth"`
Placeofbirth string `json:"placeofbirth"`
Phone string `json:"phone"`
Email string `json:"email"`
Password string `json:"password"`
Role string `json:"role"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
func (r *RegisterAdminRequest) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Name) == "" {
errors["name"] = append(errors["name"], "Name is required")
}
if strings.TrimSpace(r.Gender) == "" {
errors["gender"] = append(errors["gender"], "Gender is required")
} else if r.Gender != "male" && r.Gender != "female" {
errors["gender"] = append(errors["gender"], "Gender must be either 'male' or 'female'")
}
if strings.TrimSpace(r.Dateofbirth) == "" {
errors["dateofbirth"] = append(errors["dateofbirth"], "Date of birth is required")
}
if strings.TrimSpace(r.Placeofbirth) == "" {
errors["placeofbirth"] = append(errors["placeofbirth"], "Place of birth is required")
}
if strings.TrimSpace(r.Phone) == "" {
errors["phone"] = append(errors["phone"], "Phone is required")
} else if !IsValidPhoneNumber(r.Phone) {
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use 62 followed by 9-13 digits")
}
if strings.TrimSpace(r.Email) == "" {
errors["email"] = append(errors["email"], "Email is required")
} else if !IsValidEmail(r.Email) {
errors["email"] = append(errors["email"], "Invalid email format")
}
if len(r.Password) < 6 {
errors["password"] = append(errors["password"], "Password must be at least 6 characters")
} else if !IsValidPassword(r.Password) {
errors["password"] = append(errors["password"], "Password must contain at least one uppercase letter, one number, and one special character")
}
if r.Password != r.PasswordConfirm {
errors["password_confirm"] = append(errors["password_confirm"], "Password and confirmation do not match")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func IsValidPhoneNumber(phone string) bool {
re := regexp.MustCompile(`^62\d{9,13}$`)
return re.MatchString(phone)
}
func IsValidEmail(email string) bool {
re := regexp.MustCompile(`^[a-z0-9]+@[a-z0-9]+\.[a-z]{2,}$`)
return re.MatchString(email)
}
func IsValidPassword(password string) bool {
if len(password) < 6 {
return false
}
hasUpper := false
hasDigit := false
hasSpecial := false
for _, char := range password {
if char >= 'A' && char <= 'Z' {
hasUpper = true
} else if char >= '0' && char <= '9' {
hasDigit = true
} else if isSpecialCharacter(char) {
hasSpecial = true
}
}
return hasUpper && hasDigit && hasSpecial
}
func isSpecialCharacter(char rune) bool {
specialChars := "!@#$%^&*()-_=+[]{}|;:'\",.<>?/`~"
return strings.ContainsRune(specialChars, char)
}

View File

@ -1 +0,0 @@
package dto

View File

@ -1,133 +0,0 @@
package dto
import (
"regexp"
"rijig/utils"
)
type LoginPengelolaRequest struct {
Phone string `json:"phone"`
}
func (r *LoginPengelolaRequest) ValidateLogin() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.Phone == "" {
errors["phone"] = append(errors["phone"], "Phone number is required")
} else if !utils.IsValidPhoneNumber(r.Phone) {
errors["phone"] = append(errors["phone"], "Phone number is not valid")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type VerifLoginPengelolaRequest struct {
Phone string `json:"phone"`
Otp string `json:"verif_otp"`
}
func (r *VerifLoginPengelolaRequest) ValidateVerifLogin() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.Phone == "" {
errors["phone"] = append(errors["phone"], "Phone number is required")
} else if !utils.IsValidPhoneNumber(r.Phone) {
errors["phone"] = append(errors["phone"], "Phone number is not valid")
}
if r.Otp == "" {
errors["otp"] = append(errors["otp"], "OTP is required")
} else if len(r.Otp) != 6 {
errors["otp"] = append(errors["otp"], "OTP must be 6 digits")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type LoginPengelolaResponse struct {
UserID string `json:"user_id"`
Role string `json:"role"`
Token string `json:"token"`
}
type PengelolaIdentityCard struct {
Cardphoto string `json:"cardphoto"`
Identificationumber string `json:"identificationumber"`
Placeofbirth string `json:"placeofbirth"`
Dateofbirth string `json:"dateofbirth"`
Gender string `json:"gender"`
BloodType string `json:"bloodtype"`
District string `json:"district"`
Village string `json:"village"`
Neighbourhood string `json:"neighbourhood"`
Religion string `json:"religion"`
Maritalstatus string `json:"maritalstatus"`
Job string `json:"job"`
Citizenship string `json:"citizenship"`
Validuntil string `json:"validuntil"`
}
func (r *PengelolaIdentityCard) ValidateIDcard() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.Cardphoto == "" {
errors["cardphoto"] = append(errors["cardphoto"], "Card photo is required")
}
if r.Identificationumber == "" {
errors["identificationumber"] = append(errors["identificationumber"], "Identification number is required")
}
if r.Dateofbirth == "" {
errors["dateofbirth"] = append(errors["dateofbirth"], "Date of birth is required")
} else if !isValidDate(r.Dateofbirth) {
errors["dateofbirth"] = append(errors["dateofbirth"], "Date of birth must be in DD-MM-YYYY format")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type PengelolaCompanyProfile struct {
CompanyName string `json:"company_name"`
CompanyPhone string `json:"company_phone"`
CompanyEmail string `json:"company_email"`
}
func (r *PengelolaCompanyProfile) ValidateCompany() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.CompanyName == "" {
errors["company_name"] = append(errors["company_name"], "Company name is required")
}
if r.CompanyPhone == "" {
errors["company_phone"] = append(errors["company_phone"], "Company phone is required")
} else if !utils.IsValidPhoneNumber(r.CompanyPhone) {
errors["company_phone"] = append(errors["company_phone"], "Invalid phone number format")
}
if r.CompanyEmail == "" {
errors["company_email"] = append(errors["company_email"], "Company email is required")
} else if !utils.IsValidEmail(r.CompanyEmail) {
errors["company_email"] = append(errors["company_email"], "Invalid email format")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func isValidDate(date string) bool {
re := regexp.MustCompile(`^\d{2}-\d{2}-\d{4}$`)
return re.MatchString(date)
}

View File

@ -1 +0,0 @@
package dto

View File

@ -1,42 +0,0 @@
package dto
import (
"regexp"
"strings"
)
type RegisterRequest struct {
Phone string `json:"phone"`
}
type VerifyOTPRequest struct {
Phone string `json:"phone"`
OTP string `json:"otp"`
DeviceID string `json:"device_id"`
}
type UserDataResponse struct {
UserID string `json:"user_id"`
UserRole string `json:"user_role"`
Token string `json:"token"`
}
func (r *RegisterRequest) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Phone) == "" {
errors["phone"] = append(errors["phone"], "Phone is required")
} else if !IsValidPhoneNumber(r.Phone) {
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use 62 followed by 9-13 digits")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func IsValidPhoneNumber(phone string) bool {
re := regexp.MustCompile(`^62\d{9,13}$`)
return re.MatchString(phone)
}

View File

@ -1,29 +0,0 @@
package dto
import "strings"
type ResponseBannerDTO struct {
ID string `json:"id"`
BannerName string `json:"bannername"`
BannerImage string `json:"bannerimage"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestBannerDTO struct {
BannerName string `json:"bannername"`
BannerImage string `json:"bannerimage"`
}
func (r *RequestBannerDTO) ValidateBannerInput() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.BannerName) == "" {
errors["bannername"] = append(errors["bannername"], "nama banner harus diisi")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,111 +0,0 @@
package dto
import (
"fmt"
"strings"
)
type NearbyCollectorDTO struct {
CollectorID string `json:"collector_id"`
Name string `json:"name"`
Phone string `json:"phone"`
Rating float32 `json:"rating"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
DistanceKm float64 `json:"distance_km"`
MatchedTrash []string `json:"matched_trash_ids"`
}
type RequestCollectorDTO struct {
AddressId string `json:"address_id"`
AvaibleTrashbyCollector []RequestAvaibleTrashbyCollector `json:"avaible_trash"`
}
type RequestAvaibleTrashbyCollector struct {
TrashId string `json:"trash_id"`
TrashPrice float32 `json:"trash_price"`
}
type RequestAddAvaibleTrash struct {
AvaibleTrash []RequestAvaibleTrashbyCollector `json:"avaible_trash"`
}
type SelectCollectorRequest struct {
Collector_id string `json:"collector_id"`
}
func (r *SelectCollectorRequest) ValidateSelectCollectorRequest() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Collector_id) == "" {
errors["collector_id"] = append(errors["collector_id"], "collector_id harus diisi")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *RequestAddAvaibleTrash) ValidateRequestAddAvaibleTrash() (map[string][]string, bool) {
errors := make(map[string][]string)
if len(r.AvaibleTrash) == 0 {
errors["avaible_trash"] = append(errors["avaible_trash"], "tidak boleh kosong")
}
for i, trash := range r.AvaibleTrash {
if strings.TrimSpace(trash.TrashId) == "" {
errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)], "trash_id tidak boleh kosong")
}
if trash.TrashPrice <= 0 {
errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)], "trash_price harus lebih dari 0")
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type ResponseCollectorDTO struct {
ID string `json:"collector_id"`
UserId string `json:"user_id"`
User *UserResponseDTO `json:"user,omitempty"`
AddressId string `json:"address_id"`
Address *AddressResponseDTO `json:"address,omitempty"`
JobStatus *string `json:"job_status,omitempty"`
Rating float32 `json:"rating"`
AvaibleTrashbyCollector []ResponseAvaibleTrashByCollector `json:"avaible_trash"`
}
type ResponseAvaibleTrashByCollector struct {
ID string `json:"id"`
TrashId string `json:"trash_id"`
TrashName string `json:"trash_name"`
TrashIcon string `json:"trash_icon"`
TrashPrice float32 `json:"trash_price"`
}
func (r *RequestCollectorDTO) ValidateRequestCollector() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.AddressId) == "" {
errors["address_id"] = append(errors["address_id"], "address_id harus diisi")
}
for i, trash := range r.AvaibleTrashbyCollector {
if strings.TrimSpace(trash.TrashId) == "" {
errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)], "trash_id tidak boleh kosong")
}
if trash.TrashPrice <= 0 {
errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)], "trash_price harus lebih dari 0")
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,62 +0,0 @@
package dto
import (
"rijig/utils"
"strings"
)
type ResponseCompanyProfileDTO struct {
ID string `json:"id"`
UserID string `json:"userId"`
CompanyName string `json:"company_name"`
CompanyAddress string `json:"company_address"`
CompanyPhone string `json:"company_phone"`
CompanyEmail string `json:"company_email"`
CompanyLogo string `json:"company_logo,omitempty"`
CompanyWebsite string `json:"company_website,omitempty"`
TaxID string `json:"taxId,omitempty"`
FoundedDate string `json:"founded_date,omitempty"`
CompanyType string `json:"company_type,omitempty"`
CompanyDescription string `json:"company_description"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestCompanyProfileDTO struct {
CompanyName string `json:"company_name"`
CompanyAddress string `json:"company_address"`
CompanyPhone string `json:"company_phone"`
CompanyEmail string `json:"company_email"`
CompanyLogo string `json:"company_logo,omitempty"`
CompanyWebsite string `json:"company_website,omitempty"`
TaxID string `json:"taxId,omitempty"`
FoundedDate string `json:"founded_date,omitempty"`
CompanyType string `json:"company_type,omitempty"`
CompanyDescription string `json:"company_description"`
}
func (r *RequestCompanyProfileDTO) ValidateCompanyProfileInput() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.CompanyName) == "" {
errors["company_Name"] = append(errors["company_name"], "Company name is required")
}
if strings.TrimSpace(r.CompanyAddress) == "" {
errors["company_Address"] = append(errors["company_address"], "Company address is required")
}
if !utils.IsValidPhoneNumber(r.CompanyPhone) {
errors["company_Phone"] = append(errors["company_phone"], "nomor harus dimulai 62.. dan 8-14 digit")
}
if strings.TrimSpace(r.CompanyDescription) == "" {
errors["company_Description"] = append(errors["company_description"], "Company description is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,34 +0,0 @@
package dto
import "strings"
type RequestCoverageArea struct {
Province string `json:"province"`
Regency string `json:"regency"`
}
type ResponseCoverageArea struct {
ID string `json:"id"`
Province string `json:"province"`
Regency string `json:"regency"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
func (r *RequestCoverageArea) ValidateCoverageArea() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Province) == "" {
errors["province"] = append(errors["province"], "nama provinsi harus diisi")
}
if strings.TrimSpace(r.Regency) == "" {
errors["regency"] = append(errors["regency"], "nama regency harus diisi")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,116 +0,0 @@
package dto
import (
"strings"
)
type ResponseIdentityCardDTO struct {
ID string `json:"id"`
UserID string `json:"userId"`
Identificationumber string `json:"identificationumber"`
Placeofbirth string `json:"placeofbirth"`
Dateofbirth string `json:"dateofbirth"`
Gender string `json:"gender"`
BloodType string `json:"bloodtype"`
District string `json:"district"`
Village string `json:"village"`
Neighbourhood string `json:"neighbourhood"`
Religion string `json:"religion"`
Maritalstatus string `json:"maritalstatus"`
Job string `json:"job"`
Citizenship string `json:"citizenship"`
Validuntil string `json:"validuntil"`
Cardphoto string `json:"cardphoto"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestIdentityCardDTO struct {
UserID string `json:"userId"`
Identificationumber string `json:"identificationumber"`
Placeofbirth string `json:"placeofbirth"`
Dateofbirth string `json:"dateofbirth"`
Gender string `json:"gender"`
BloodType string `json:"bloodtype"`
District string `json:"district"`
Village string `json:"village"`
Neighbourhood string `json:"neighbourhood"`
Religion string `json:"religion"`
Maritalstatus string `json:"maritalstatus"`
Job string `json:"job"`
Citizenship string `json:"citizenship"`
Validuntil string `json:"validuntil"`
Cardphoto string `json:"cardphoto"`
}
func (r *RequestIdentityCardDTO) ValidateIdentityCardInput() (map[string][]string, bool) {
errors := make(map[string][]string)
isValid := true
if strings.TrimSpace(r.Identificationumber) == "" {
errors["identificationumber"] = append(errors["identificationumber"], "Nomor identifikasi harus diisi")
isValid = false
}
if strings.TrimSpace(r.Placeofbirth) == "" {
errors["placeofbirth"] = append(errors["placeofbirth"], "Tempat lahir harus diisi")
isValid = false
}
if strings.TrimSpace(r.Dateofbirth) == "" {
errors["dateofbirth"] = append(errors["dateofbirth"], "Tanggal lahir harus diisi")
isValid = false
}
if strings.TrimSpace(r.Gender) == "" {
errors["gender"] = append(errors["gender"], "Jenis kelamin harus diisi")
isValid = false
}
if strings.TrimSpace(r.BloodType) == "" {
errors["bloodtype"] = append(errors["bloodtype"], "Golongan darah harus diisi")
isValid = false
}
if strings.TrimSpace(r.District) == "" {
errors["district"] = append(errors["district"], "Kecamatan harus diisi")
isValid = false
}
if strings.TrimSpace(r.Village) == "" {
errors["village"] = append(errors["village"], "Desa harus diisi")
isValid = false
}
if strings.TrimSpace(r.Neighbourhood) == "" {
errors["neighbourhood"] = append(errors["neighbourhood"], "RT/RW harus diisi")
isValid = false
}
if strings.TrimSpace(r.Religion) == "" {
errors["religion"] = append(errors["religion"], "Agama harus diisi")
isValid = false
}
if strings.TrimSpace(r.Maritalstatus) == "" {
errors["maritalstatus"] = append(errors["maritalstatus"], "Status pernikahan harus diisi")
isValid = false
}
if strings.TrimSpace(r.Job) == "" {
errors["job"] = append(errors["job"], "Pekerjaan harus diisi")
isValid = false
}
if strings.TrimSpace(r.Citizenship) == "" {
errors["citizenship"] = append(errors["citizenship"], "Kewarganegaraan harus diisi")
isValid = false
}
if strings.TrimSpace(r.Validuntil) == "" {
errors["validuntil"] = append(errors["validuntil"], "Masa berlaku harus diisi")
isValid = false
}
return errors, isValid
}

View File

@ -1,34 +0,0 @@
package dto
import "strings"
type ReponseInitialCointDTO struct {
ID string `json:"coin_id"`
CoinName string `json:"coin_name"`
ValuePerUnit float64 `json:"value_perunit"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestInitialCointDTO struct {
CoinName string `json:"coin_name"`
ValuePerUnit float64 `json:"value_perunit"`
}
func (r *RequestInitialCointDTO) ValidateCointInput() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.CoinName) == "" {
errors["coin_name"] = append(errors["coin_name"], "nama coin harus diisi")
}
if r.ValuePerUnit <= 0 {
errors["value_perunit"] = append(errors["value_perunit"], "value per unit harus lebih besar dari 0")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,55 +0,0 @@
package dto
import (
"mime/multipart"
"regexp"
"strings"
)
type ResponseProductImageDTO struct {
ID string `json:"id"`
ProductID string `json:"productId"`
ImageURL string `json:"imageURL"`
}
type ResponseProductDTO struct {
ID string `json:"id"`
StoreID string `json:"storeId"`
ProductName string `json:"productName"`
Quantity int `json:"quantity"`
Saled int `json:"saled"`
ProductImages []ResponseProductImageDTO `json:"productImages,omitempty"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestProductDTO struct {
ProductName string `json:"product_name"`
Quantity int `json:"quantity"`
ProductImages []*multipart.FileHeader `json:"product_images,omitempty"`
}
func (r *RequestProductDTO) ValidateProductInput() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.ProductName) == "" {
errors["product_name"] = append(errors["product_name"], "Product name is required")
} else if len(r.ProductName) < 3 {
errors["product_name"] = append(errors["product_name"], "Product name must be at least 3 characters long")
} else {
validNameRegex := `^[a-zA-Z0-9\s_.-]+$`
if matched, _ := regexp.MatchString(validNameRegex, r.ProductName); !matched {
errors["product_name"] = append(errors["product_name"], "Product name can only contain letters, numbers, spaces, underscores, and dashes")
}
}
if r.Quantity < 1 {
errors["quantity"] = append(errors["quantity"], "Quantity must be at least 1")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,25 +0,0 @@
package dto
import "strings"
type CreatePickupRatingDTO struct {
Rating float32 `json:"rating"`
Feedback string `json:"feedback"`
}
func (r *CreatePickupRatingDTO) ValidateCreatePickupRatingDTO() (map[string][]string, bool) {
errors := make(map[string][]string)
if r.Rating < 1.0 || r.Rating > 5.0 {
errors["rating"] = append(errors["rating"], "Rating harus antara 1.0 sampai 5.0")
}
if len(strings.TrimSpace(r.Feedback)) > 255 {
errors["feedback"] = append(errors["feedback"], "Feedback tidak boleh lebih dari 255 karakter")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,74 +0,0 @@
package dto
import (
"strings"
)
type SelectCollectorDTO struct {
CollectorID string `json:"collector_id"`
}
type UpdateRequestPickupItemDTO struct {
ItemID string `json:"item_id"`
Amount float64 `json:"actual_amount"`
}
type UpdatePickupItemsRequest struct {
Items []UpdateRequestPickupItemDTO `json:"items"`
}
func (r *SelectCollectorDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.CollectorID) == "" {
errors["collector_id"] = append(errors["collector_id"], "collector_id tidak boleh kosong")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type AssignedPickupDTO struct {
PickupID string `json:"pickup_id"`
UserID string `json:"user_id"`
UserName string `json:"user_name"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
Notes string `json:"notes"`
MatchedTrash []string `json:"matched_trash"`
}
type PickupRequestForCollectorDTO struct {
PickupID string `json:"pickup_id"`
UserID string `json:"user_id"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
DistanceKm float64 `json:"distance_km"`
MatchedTrash []string `json:"matched_trash"`
}
type RequestPickupDTO struct {
AddressID string `json:"address_id"`
RequestMethod string `json:"request_method"`
Notes string `json:"notes,omitempty"`
}
func (r *RequestPickupDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.AddressID) == "" {
errors["address_id"] = append(errors["address_id"], "alamat harus dipilih")
}
method := strings.ToLower(strings.TrimSpace(r.RequestMethod))
if method != "manual" && method != "otomatis" {
errors["request_method"] = append(errors["request_method"], "harus manual atau otomatis")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,88 +0,0 @@
package dto
import (
"fmt"
"strings"
)
type RequestPickup struct {
AddressID string `json:"address_id"`
RequestMethod string `json:"request_method"`
EvidenceImage string `json:"evidence_image"`
Notes string `json:"notes"`
RequestItems []RequestPickupItem `json:"request_items"`
}
type RequestPickupItem struct {
TrashCategoryID string `json:"trash_category_id"`
EstimatedAmount float64 `json:"estimated_amount"`
}
type ResponseRequestPickup struct {
ID string `json:"id,omitempty"`
UserId string `json:"user_id,omitempty"`
User []UserResponseDTO `json:"user,omitempty"`
AddressID string `json:"address_id,omitempty"`
Address []AddressResponseDTO `json:"address,omitempty"`
EvidenceImage string `json:"evidence_image,omitempty"`
Notes string `json:"notes,omitempty"`
StatusPickup string `json:"status_pickup,omitempty"`
CollectorID string `json:"collectorid,omitempty"`
Collector []ResponseCollectorDTO `json:"collector,omitempty"`
ConfirmedByCollectorAt string `json:"confirmedat,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
RequestItems []ResponseRequestPickupItem `json:"request_items,omitempty"`
}
type ResponseRequestPickupItem struct {
ID string `json:"id,omitempty"`
TrashCategoryID string `json:"trash_category_id,omitempty"`
// TrashCategory []ResponseTrashCategoryDTO `json:"trash_category,omitempty"`
EstimatedAmount float64 `json:"estimated_amount,omitempty"`
}
func (r *RequestPickup) ValidateRequestPickup() (map[string][]string, bool) {
errors := make(map[string][]string)
if len(r.RequestItems) == 0 {
errors["request_items"] = append(errors["request_items"], "At least one item must be provided")
}
if strings.TrimSpace(r.AddressID) == "" {
errors["address_id"] = append(errors["address_id"], "Address ID must be provided")
}
for i, item := range r.RequestItems {
itemErrors, valid := item.ValidateRequestPickupItem(i)
if !valid {
for field, msgs := range itemErrors {
errors[field] = append(errors[field], msgs...)
}
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *RequestPickupItem) ValidateRequestPickupItem(index int) (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.TrashCategoryID) == "" {
errors["trash_category_id"] = append(errors["trash_category_id"], fmt.Sprintf("Trash category ID cannot be empty (Item %d)", index+1))
}
if r.EstimatedAmount < 2 {
errors["estimated_amount"] = append(errors["estimated_amount"], fmt.Sprintf("Estimated amount must be >= 2.0 kg (Item %d)", index+1))
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,8 +0,0 @@
package dto
type RoleResponseDTO struct {
ID string `json:"role_id"`
RoleName string `json:"role_name"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}

View File

@ -1,68 +0,0 @@
package dto
import (
"regexp"
"strings"
)
type ResponseStoreDTO struct {
ID string `json:"id"`
UserID string `json:"userId"`
StoreName string `json:"storeName"`
StoreLogo string `json:"storeLogo"`
StoreBanner string `json:"storeBanner"`
StoreInfo string `json:"storeInfo"`
StoreAddressID string `json:"storeAddressId"`
TotalProduct int `json:"TotalProduct"`
Followers int `json:"followers"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestStoreDTO struct {
StoreName string `json:"store_name"`
StoreLogo string `json:"store_logo"`
StoreBanner string `json:"store_banner"`
StoreInfo string `json:"store_info"`
StoreAddressID string `json:"store_address_id"`
}
func (r *RequestStoreDTO) ValidateStoreInput() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.StoreName) == "" {
errors["store_name"] = append(errors["store_name"], "Store name is required")
} else if len(r.StoreName) < 3 {
errors["store_name"] = append(errors["store_name"], "Store name must be at least 3 characters long")
} else {
validNameRegex := `^[a-zA-Z0-9_.\s]+$`
if matched, _ := regexp.MatchString(validNameRegex, r.StoreName); !matched {
errors["store_name"] = append(errors["store_name"], "Store name can only contain letters, numbers, underscores, and periods")
}
}
if strings.TrimSpace(r.StoreLogo) == "" {
errors["store_logo"] = append(errors["store_logo"], "Store logo is required")
}
if strings.TrimSpace(r.StoreBanner) == "" {
errors["store_banner"] = append(errors["store_banner"], "Store banner is required")
}
if strings.TrimSpace(r.StoreInfo) == "" {
errors["store_info"] = append(errors["store_info"], "Store info is required")
}
uuidRegex := `^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$`
if r.StoreAddressID == "" {
errors["store_address_id"] = append(errors["store_address_id"], "Store address ID is required")
} else if matched, _ := regexp.MatchString(uuidRegex, r.StoreAddressID); !matched {
errors["store_address_id"] = append(errors["store_address_id"], "Invalid Store Address ID format")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,65 +0,0 @@
package dto
/*
import (
"strings"
)
type RequestTrashCategoryDTO struct {
Name string `json:"name"`
EstimatedPrice string `json:"estimatedprice"`
Icon string `json:"icon"`
}
type ResponseTrashCategoryDTO struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Icon string `json:"icon,omitempty"`
EstimatedPrice float64 `json:"estimatedprice"`
CreatedAt string `json:"createdAt,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
Details []ResponseTrashDetailDTO `json:"details,omitempty"`
}
type ResponseTrashDetailDTO struct {
ID string `json:"id"`
CategoryID string `json:"category_id"`
Description string `json:"description"`
Price float64 `json:"price"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RequestTrashDetailDTO struct {
CategoryID string `json:"category_id"`
Description string `json:"description"`
Price float64 `json:"price"`
}
func (r *RequestTrashCategoryDTO) ValidateTrashCategoryInput() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Name) == "" {
errors["name"] = append(errors["name"], "name is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func (r *RequestTrashDetailDTO) ValidateTrashDetailInput() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Description) == "" {
errors["description"] = append(errors["description"], "description is required")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
*/

View File

@ -1,49 +0,0 @@
package dto
import (
"fmt"
"strings"
)
type RequestCartItemDTO struct {
TrashID string `json:"trash_id"`
Amount float64 `json:"amount"`
}
type RequestCartDTO struct {
CartItems []RequestCartItemDTO `json:"cart_items"`
}
type ResponseCartDTO struct {
ID string `json:"id"`
UserID string `json:"user_id"`
TotalAmount float64 `json:"total_amount"`
EstimatedTotalPrice float64 `json:"estimated_total_price"`
CartItems []ResponseCartItemDTO `json:"cart_items"`
}
type ResponseCartItemDTO struct {
ID string `json:"id"`
TrashID string `json:"trash_id"`
TrashName string `json:"trash_name"`
TrashIcon string `json:"trash_icon"`
TrashPrice float64 `json:"trash_price"`
Amount float64 `json:"amount"`
SubTotalEstimatedPrice float64 `json:"subtotal_estimated_price"`
}
func (r *RequestCartDTO) ValidateRequestCartDTO() (map[string][]string, bool) {
errors := make(map[string][]string)
for i, item := range r.CartItems {
if strings.TrimSpace(item.TrashID) == "" {
errors[fmt.Sprintf("cart_items[%d].trash_id", i)] = append(errors[fmt.Sprintf("cart_items[%d].trash_id", i)], "trash_id tidak boleh kosong")
}
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,81 +0,0 @@
package dto
import (
"rijig/utils"
"strings"
)
type UserResponseDTO struct {
ID string `json:"id,omitempty"`
Username string `json:"username,omitempty"`
Avatar *string `json:"photoprofile,omitempty"`
Name string `json:"name,omitempty"`
Phone string `json:"phone,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"emailVerified,omitempty"`
RoleName string `json:"role,omitempty"`
CreatedAt string `json:"createdAt,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
}
type RequestUserDTO struct {
Name string `json:"name"`
Phone string `json:"phone"`
Email string `json:"email"`
}
func (r *RequestUserDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Name) == "" {
errors["name"] = append(errors["name"], "Name is required")
}
if strings.TrimSpace(r.Phone) == "" {
errors["phone"] = append(errors["phone"], "Phone number is required")
} else if !utils.IsValidPhoneNumber(r.Phone) {
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits")
}
if strings.TrimSpace(r.Email) != "" && !utils.IsValidEmail(r.Email) {
errors["email"] = append(errors["email"], "Invalid email format")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type UpdatePasswordDTO struct {
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
ConfirmNewPassword string `json:"confirm_new_password"`
}
func (u *UpdatePasswordDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if u.OldPassword == "" {
errors["old_password"] = append(errors["old_password"], "Old password is required")
}
if u.NewPassword == "" {
errors["new_password"] = append(errors["new_password"], "New password is required")
} else if !utils.IsValidPassword(u.NewPassword) {
errors["new_password"] = append(errors["new_password"], "Password must contain at least one uppercase letter, one digit, and one special character")
}
if u.ConfirmNewPassword == "" {
errors["confirm_new_password"] = append(errors["confirm_new_password"], "Confirm new password is required")
} else if u.NewPassword != u.ConfirmNewPassword {
errors["confirm_new_password"] = append(errors["confirm_new_password"], "Passwords do not match")
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}

View File

@ -1,66 +0,0 @@
package dto
import (
"fmt"
"regexp"
"strings"
)
type RequestUserPinDTO struct {
Pin string `json:"userpin"`
}
func (r *RequestUserPinDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(r.Pin) == "" {
errors["pin"] = append(errors["pin"], "Pin is required")
}
if err := validatePin(r.Pin); err != nil {
errors["pin"] = append(errors["pin"], err.Error())
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
type UpdateUserPinDTO struct {
OldPin string `json:"old_pin"`
NewPin string `json:"new_pin"`
}
func (u *UpdateUserPinDTO) Validate() (map[string][]string, bool) {
errors := make(map[string][]string)
if strings.TrimSpace(u.OldPin) == "" {
errors["old_pin"] = append(errors["old_pin"], "Old pin is required")
}
if err := validatePin(u.NewPin); err != nil {
errors["new_pin"] = append(errors["new_pin"], err.Error())
}
if len(errors) > 0 {
return errors, false
}
return nil, true
}
func isNumeric(s string) bool {
re := regexp.MustCompile(`^[0-9]+$`)
return re.MatchString(s)
}
func validatePin(pin string) error {
if len(pin) != 6 {
return fmt.Errorf("pin harus terdiri dari 6 digit")
} else if !isNumeric(pin) {
return fmt.Errorf("pin harus berupa angka")
}
return nil
}

View File

@ -1,27 +0,0 @@
package dto
type ProvinceResponseDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Regencies []RegencyResponseDTO `json:"regencies,omitempty"`
}
type RegencyResponseDTO struct {
ID string `json:"id"`
ProvinceID string `json:"province_id"`
Name string `json:"name"`
Districts []DistrictResponseDTO `json:"districts,omitempty"`
}
type DistrictResponseDTO struct {
ID string `json:"id"`
RegencyID string `json:"regency_id"`
Name string `json:"name"`
Villages []VillageResponseDTO `json:"villages,omitempty"`
}
type VillageResponseDTO struct {
ID string `json:"id"`
DistrictID string `json:"district_id"`
Name string `json:"name"`
}

View File

@ -3,7 +3,6 @@ package about
import ( import (
"fmt" "fmt"
"log" "log"
"rijig/dto"
"rijig/utils" "rijig/utils"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -20,7 +19,7 @@ func NewAboutHandler(aboutService AboutService) *AboutHandler {
} }
func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error { func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error {
var request dto.RequestAboutDTO var request RequestAboutDTO
if err := c.BodyParser(&request); err != nil { if err := c.BodyParser(&request); err != nil {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Invalid request body", map[string][]string{"body": {"Invalid body"}}) return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Invalid request body", map[string][]string{"body": {"Invalid body"}})
} }
@ -47,7 +46,7 @@ func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error {
func (h *AboutHandler) UpdateAbout(c *fiber.Ctx) error { func (h *AboutHandler) UpdateAbout(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
var request dto.RequestAboutDTO var request RequestAboutDTO
if err := c.BodyParser(&request); err != nil { if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing request body: %v", err) log.Printf("Error parsing request body: %v", err)
return utils.BadRequest(c, "Invalid input data") return utils.BadRequest(c, "Invalid input data")
@ -114,7 +113,7 @@ func (h *AboutHandler) DeleteAbout(c *fiber.Ctx) error {
} }
func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error { func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error {
var request dto.RequestAboutDetailDTO var request RequestAboutDetailDTO
if err := c.BodyParser(&request); err != nil { if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing request body: %v", err) log.Printf("Error parsing request body: %v", err)
return utils.BadRequest(c, "Invalid input data") return utils.BadRequest(c, "Invalid input data")
@ -143,7 +142,7 @@ func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error {
func (h *AboutHandler) UpdateAboutDetail(c *fiber.Ctx) error { func (h *AboutHandler) UpdateAboutDetail(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
var request dto.RequestAboutDetailDTO var request RequestAboutDetailDTO
if err := c.BodyParser(&request); err != nil { if err := c.BodyParser(&request); err != nil {
log.Printf("Error parsing request body: %v", err) log.Printf("Error parsing request body: %v", err)
return utils.BadRequest(c, "Invalid input data") return utils.BadRequest(c, "Invalid input data")

View File

@ -7,7 +7,6 @@ import (
"mime/multipart" "mime/multipart"
"os" "os"
"path/filepath" "path/filepath"
"rijig/dto"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
"time" "time"
@ -24,15 +23,15 @@ const (
) )
type AboutService interface { type AboutService interface {
CreateAbout(ctx context.Context, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) CreateAbout(ctx context.Context, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error)
UpdateAbout(ctx context.Context, id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) UpdateAbout(ctx context.Context, id string, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error)
GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO, error) GetAllAbout(ctx context.Context) ([]ResponseAboutDTO, error)
GetAboutByID(ctx context.Context, id string) (*dto.ResponseAboutDTO, error) GetAboutByID(ctx context.Context, id string) (*ResponseAboutDTO, error)
GetAboutDetailById(ctx context.Context, id string) (*dto.ResponseAboutDetailDTO, error) GetAboutDetailById(ctx context.Context, id string) (*ResponseAboutDetailDTO, error)
DeleteAbout(ctx context.Context, id string) error DeleteAbout(ctx context.Context, id string) error
CreateAboutDetail(ctx context.Context, request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) CreateAboutDetail(ctx context.Context, request RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error)
UpdateAboutDetail(ctx context.Context, id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) UpdateAboutDetail(ctx context.Context, id string, request RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error)
DeleteAboutDetail(ctx context.Context, id string) error DeleteAboutDetail(ctx context.Context, id string) error
} }
@ -66,11 +65,11 @@ func (s *aboutService) invalidateAboutDetailCaches(aboutDetailID, aboutID string
s.invalidateAboutCaches(aboutID) s.invalidateAboutCaches(aboutID)
} }
func formatResponseAboutDetailDTO(about *model.AboutDetail) (*dto.ResponseAboutDetailDTO, error) { func formatResponseAboutDetailDTO(about *model.AboutDetail) (*ResponseAboutDetailDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt) createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt)
response := &dto.ResponseAboutDetailDTO{ response := &ResponseAboutDetailDTO{
ID: about.ID, ID: about.ID,
AboutID: about.AboutID, AboutID: about.AboutID,
ImageDetail: about.ImageDetail, ImageDetail: about.ImageDetail,
@ -82,11 +81,11 @@ func formatResponseAboutDetailDTO(about *model.AboutDetail) (*dto.ResponseAboutD
return response, nil return response, nil
} }
func formatResponseAboutDTO(about *model.About) (*dto.ResponseAboutDTO, error) { func formatResponseAboutDTO(about *model.About) (*ResponseAboutDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt) createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt)
response := &dto.ResponseAboutDTO{ response := &ResponseAboutDTO{
ID: about.ID, ID: about.ID,
Title: about.Title, Title: about.Title,
CoverImage: about.CoverImage, CoverImage: about.CoverImage,
@ -194,7 +193,7 @@ func deleteCoverImageAbout(coverimageAboutPath string) error {
return nil return nil
} }
func (s *aboutService) CreateAbout(ctx context.Context, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) { func (s *aboutService) CreateAbout(ctx context.Context, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error) {
errors, valid := request.ValidateAbout() errors, valid := request.ValidateAbout()
if !valid { if !valid {
return nil, fmt.Errorf("validation error: %v", errors) return nil, fmt.Errorf("validation error: %v", errors)
@ -224,7 +223,7 @@ func (s *aboutService) CreateAbout(ctx context.Context, request dto.RequestAbout
return response, nil return response, nil
} }
func (s *aboutService) UpdateAbout(ctx context.Context, id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) { func (s *aboutService) UpdateAbout(ctx context.Context, id string, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error) {
errors, valid := request.ValidateAbout() errors, valid := request.ValidateAbout()
if !valid { if !valid {
return nil, fmt.Errorf("validation error: %v", errors) return nil, fmt.Errorf("validation error: %v", errors)
@ -271,9 +270,9 @@ func (s *aboutService) UpdateAbout(ctx context.Context, id string, request dto.R
return response, nil return response, nil
} }
func (s *aboutService) GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO, error) { func (s *aboutService) GetAllAbout(ctx context.Context) ([]ResponseAboutDTO, error) {
var cachedAbouts []dto.ResponseAboutDTO var cachedAbouts []ResponseAboutDTO
if err := utils.GetCache(cacheKeyAllAbout, &cachedAbouts); err == nil { if err := utils.GetCache(cacheKeyAllAbout, &cachedAbouts); err == nil {
return cachedAbouts, nil return cachedAbouts, nil
} }
@ -283,7 +282,7 @@ func (s *aboutService) GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO,
return nil, fmt.Errorf("failed to get About list: %v", err) return nil, fmt.Errorf("failed to get About list: %v", err)
} }
var aboutDTOList []dto.ResponseAboutDTO var aboutDTOList []ResponseAboutDTO
for _, about := range aboutList { for _, about := range aboutList {
response, err := formatResponseAboutDTO(&about) response, err := formatResponseAboutDTO(&about)
if err != nil { if err != nil {
@ -300,10 +299,10 @@ func (s *aboutService) GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO,
return aboutDTOList, nil return aboutDTOList, nil
} }
func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*dto.ResponseAboutDTO, error) { func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*ResponseAboutDTO, error) {
cacheKey := fmt.Sprintf(cacheKeyAboutByID, id) cacheKey := fmt.Sprintf(cacheKeyAboutByID, id)
var cachedAbout dto.ResponseAboutDTO var cachedAbout ResponseAboutDTO
if err := utils.GetCache(cacheKey, &cachedAbout); err == nil { if err := utils.GetCache(cacheKey, &cachedAbout); err == nil {
return &cachedAbout, nil return &cachedAbout, nil
} }
@ -318,7 +317,7 @@ func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*dto.Respon
return nil, fmt.Errorf("error formatting About response: %v", err) return nil, fmt.Errorf("error formatting About response: %v", err)
} }
var responseDetails []dto.ResponseAboutDetailDTO var responseDetails []ResponseAboutDetailDTO
for _, detail := range about.AboutDetail { for _, detail := range about.AboutDetail {
formattedDetail, err := formatResponseAboutDetailDTO(&detail) formattedDetail, err := formatResponseAboutDetailDTO(&detail)
if err != nil { if err != nil {
@ -336,10 +335,10 @@ func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*dto.Respon
return response, nil return response, nil
} }
func (s *aboutService) GetAboutDetailById(ctx context.Context, id string) (*dto.ResponseAboutDetailDTO, error) { func (s *aboutService) GetAboutDetailById(ctx context.Context, id string) (*ResponseAboutDetailDTO, error) {
cacheKey := fmt.Sprintf(cacheKeyAboutDetail, id) cacheKey := fmt.Sprintf(cacheKeyAboutDetail, id)
var cachedDetail dto.ResponseAboutDetailDTO var cachedDetail ResponseAboutDetailDTO
if err := utils.GetCache(cacheKey, &cachedDetail); err == nil { if err := utils.GetCache(cacheKey, &cachedDetail); err == nil {
return &cachedDetail, nil return &cachedDetail, nil
} }
@ -390,7 +389,7 @@ func (s *aboutService) DeleteAbout(ctx context.Context, id string) error {
return nil return nil
} }
func (s *aboutService) CreateAboutDetail(ctx context.Context, request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) { func (s *aboutService) CreateAboutDetail(ctx context.Context, request RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error) {
errors, valid := request.ValidateAboutDetail() errors, valid := request.ValidateAboutDetail()
if !valid { if !valid {
return nil, fmt.Errorf("validation error: %v", errors) return nil, fmt.Errorf("validation error: %v", errors)
@ -426,7 +425,7 @@ func (s *aboutService) CreateAboutDetail(ctx context.Context, request dto.Reques
return response, nil return response, nil
} }
func (s *aboutService) UpdateAboutDetail(ctx context.Context, id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) { func (s *aboutService) UpdateAboutDetail(ctx context.Context, id string, request RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error) {
errors, valid := request.ValidateAboutDetail() errors, valid := request.ValidateAboutDetail()
if !valid { if !valid {
return nil, fmt.Errorf("validation error: %v", errors) return nil, fmt.Errorf("validation error: %v", errors)

View File

@ -1,7 +1,6 @@
package address package address
import ( import (
"rijig/dto"
"rijig/middleware" "rijig/middleware"
"rijig/utils" "rijig/utils"
@ -17,7 +16,7 @@ func NewAddressHandler(addressService AddressService) *AddressHandler {
} }
func (h *AddressHandler) CreateAddress(c *fiber.Ctx) error { func (h *AddressHandler) CreateAddress(c *fiber.Ctx) error {
var request dto.CreateAddressDTO var request CreateAddressDTO
claims, err := middleware.GetUserFromContext(c) claims, err := middleware.GetUserFromContext(c)
if err != nil { if err != nil {
return err return err
@ -74,7 +73,7 @@ func (h *AddressHandler) UpdateAddress(c *fiber.Ctx) error {
addressID := c.Params("address_id") addressID := c.Params("address_id")
var request dto.CreateAddressDTO var request CreateAddressDTO
claims, err := middleware.GetUserFromContext(c) claims, err := middleware.GetUserFromContext(c)
if err != nil { if err != nil {
return err return err

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"time" "time"
"rijig/dto"
"rijig/internal/wilayahindo" "rijig/internal/wilayahindo"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
@ -20,10 +19,10 @@ const (
) )
type AddressService interface { type AddressService interface {
CreateAddress(ctx context.Context, userID string, request dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) CreateAddress(ctx context.Context, userID string, request CreateAddressDTO) (*AddressResponseDTO, error)
GetAddressByUserID(ctx context.Context, userID string) ([]dto.AddressResponseDTO, error) GetAddressByUserID(ctx context.Context, userID string) ([]AddressResponseDTO, error)
GetAddressByID(ctx context.Context, userID, id string) (*dto.AddressResponseDTO, error) GetAddressByID(ctx context.Context, userID, id string) (*AddressResponseDTO, error)
UpdateAddress(ctx context.Context, userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) UpdateAddress(ctx context.Context, userID, id string, addressDTO CreateAddressDTO) (*AddressResponseDTO, error)
DeleteAddress(ctx context.Context, userID, id string) error DeleteAddress(ctx context.Context, userID, id string) error
} }
@ -39,7 +38,7 @@ func NewAddressService(addressRepo AddressRepository, wilayahRepo wilayahindo.Wi
} }
} }
func (s *addressService) validateWilayahIDs(ctx context.Context, addressDTO dto.CreateAddressDTO) (string, string, string, string, error) { func (s *addressService) validateWilayahIDs(ctx context.Context, addressDTO CreateAddressDTO) (string, string, string, string, error) {
province, _, err := s.wilayahRepo.FindProvinceByID(ctx, addressDTO.Province, 0, 0) province, _, err := s.wilayahRepo.FindProvinceByID(ctx, addressDTO.Province, 0, 0)
if err != nil { if err != nil {
@ -64,11 +63,11 @@ func (s *addressService) validateWilayahIDs(ctx context.Context, addressDTO dto.
return province.Name, regency.Name, district.Name, village.Name, nil return province.Name, regency.Name, district.Name, village.Name, nil
} }
func (s *addressService) mapToResponseDTO(address *model.Address) *dto.AddressResponseDTO { func (s *addressService) mapToResponseDTO(address *model.Address) *AddressResponseDTO {
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt) createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
return &dto.AddressResponseDTO{ return &AddressResponseDTO{
UserID: address.UserID, UserID: address.UserID,
ID: address.ID, ID: address.ID,
Province: address.Province, Province: address.Province,
@ -98,20 +97,20 @@ func (s *addressService) invalidateAddressCaches(userID, addressID string) {
} }
} }
func (s *addressService) cacheAddress(addressDTO *dto.AddressResponseDTO) { func (s *addressService) cacheAddress(addressDTO *AddressResponseDTO) {
cacheKey := fmt.Sprintf(addressCacheKeyPattern, addressDTO.ID) cacheKey := fmt.Sprintf(addressCacheKeyPattern, addressDTO.ID)
if err := utils.SetCache(cacheKey, addressDTO, cacheTTL); err != nil { if err := utils.SetCache(cacheKey, addressDTO, cacheTTL); err != nil {
fmt.Printf("Error caching address to Redis: %v\n", err) fmt.Printf("Error caching address to Redis: %v\n", err)
} }
} }
func (s *addressService) cacheUserAddresses(ctx context.Context, userID string) ([]dto.AddressResponseDTO, error) { func (s *addressService) cacheUserAddresses(ctx context.Context, userID string) ([]AddressResponseDTO, error) {
addresses, err := s.addressRepo.FindAddressByUserID(ctx, userID) addresses, err := s.addressRepo.FindAddressByUserID(ctx, userID)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch addresses: %w", err) return nil, fmt.Errorf("failed to fetch addresses: %w", err)
} }
var addressDTOs []dto.AddressResponseDTO var addressDTOs []AddressResponseDTO
for _, address := range addresses { for _, address := range addresses {
addressDTOs = append(addressDTOs, *s.mapToResponseDTO(&address)) addressDTOs = append(addressDTOs, *s.mapToResponseDTO(&address))
} }
@ -137,7 +136,7 @@ func (s *addressService) checkAddressOwnership(ctx context.Context, userID, addr
return address, nil return address, nil
} }
func (s *addressService) CreateAddress(ctx context.Context, userID string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) { func (s *addressService) CreateAddress(ctx context.Context, userID string, addressDTO CreateAddressDTO) (*AddressResponseDTO, error) {
provinceName, regencyName, districtName, villageName, err := s.validateWilayahIDs(ctx, addressDTO) provinceName, regencyName, districtName, villageName, err := s.validateWilayahIDs(ctx, addressDTO)
if err != nil { if err != nil {
@ -168,10 +167,10 @@ func (s *addressService) CreateAddress(ctx context.Context, userID string, addre
return responseDTO, nil return responseDTO, nil
} }
func (s *addressService) GetAddressByUserID(ctx context.Context, userID string) ([]dto.AddressResponseDTO, error) { func (s *addressService) GetAddressByUserID(ctx context.Context, userID string) ([]AddressResponseDTO, error) {
cacheKey := fmt.Sprintf(userAddressesCacheKeyPattern, userID) cacheKey := fmt.Sprintf(userAddressesCacheKeyPattern, userID)
var cachedAddresses []dto.AddressResponseDTO var cachedAddresses []AddressResponseDTO
if err := utils.GetCache(cacheKey, &cachedAddresses); err == nil { if err := utils.GetCache(cacheKey, &cachedAddresses); err == nil {
return cachedAddresses, nil return cachedAddresses, nil
@ -180,7 +179,7 @@ func (s *addressService) GetAddressByUserID(ctx context.Context, userID string)
return s.cacheUserAddresses(ctx, userID) return s.cacheUserAddresses(ctx, userID)
} }
func (s *addressService) GetAddressByID(ctx context.Context, userID, id string) (*dto.AddressResponseDTO, error) { func (s *addressService) GetAddressByID(ctx context.Context, userID, id string) (*AddressResponseDTO, error) {
address, err := s.checkAddressOwnership(ctx, userID, id) address, err := s.checkAddressOwnership(ctx, userID, id)
if err != nil { if err != nil {
@ -188,7 +187,7 @@ func (s *addressService) GetAddressByID(ctx context.Context, userID, id string)
} }
cacheKey := fmt.Sprintf(addressCacheKeyPattern, id) cacheKey := fmt.Sprintf(addressCacheKeyPattern, id)
var cachedAddress dto.AddressResponseDTO var cachedAddress AddressResponseDTO
if err := utils.GetCache(cacheKey, &cachedAddress); err == nil { if err := utils.GetCache(cacheKey, &cachedAddress); err == nil {
return &cachedAddress, nil return &cachedAddress, nil
@ -200,7 +199,7 @@ func (s *addressService) GetAddressByID(ctx context.Context, userID, id string)
return responseDTO, nil return responseDTO, nil
} }
func (s *addressService) UpdateAddress(ctx context.Context, userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) { func (s *addressService) UpdateAddress(ctx context.Context, userID, id string, addressDTO CreateAddressDTO) (*AddressResponseDTO, error) {
address, err := s.checkAddressOwnership(ctx, userID, id) address, err := s.checkAddressOwnership(ctx, userID, id)
if err != nil { if err != nil {

View File

@ -2,6 +2,8 @@ package authentication
import ( import (
"context" "context"
"fmt"
"log"
"rijig/model" "rijig/model"
"gorm.io/gorm" "gorm.io/gorm"
@ -15,6 +17,9 @@ type AuthenticationRepository interface {
CreateUser(ctx context.Context, user *model.User) error CreateUser(ctx context.Context, user *model.User) error
UpdateUser(ctx context.Context, user *model.User) error UpdateUser(ctx context.Context, user *model.User) error
PatchUser(ctx context.Context, userID string, updates map[string]interface{}) error PatchUser(ctx context.Context, userID string, updates map[string]interface{}) error
GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error)
GetCompanyProfilesByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.CompanyProfile, error)
} }
type authenticationRepository struct { type authenticationRepository struct {
@ -84,3 +89,37 @@ func (r *authenticationRepository) PatchUser(ctx context.Context, userID string,
Where("id = ?", userID). Where("id = ?", userID).
Updates(updates).Error Updates(updates).Error
} }
func (r *authenticationRepository) GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) {
var identityCards []model.IdentityCard
if err := r.db.WithContext(ctx).
Joins("JOIN users ON identity_cards.user_id = users.id").
Where("users.registration_status = ?", userRegStatus).
Preload("User").
Preload("User.Role").
Find(&identityCards).Error; err != nil {
log.Printf("Error fetching identity cards by user registration status: %v", err)
return nil, fmt.Errorf("error fetching identity cards by user registration status: %w", err)
}
log.Printf("Found %d identity cards with registration status: %s", len(identityCards), userRegStatus)
return identityCards, nil
}
func (r *authenticationRepository) GetCompanyProfilesByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.CompanyProfile, error) {
var companyProfiles []model.CompanyProfile
if err := r.db.WithContext(ctx).
Joins("JOIN users ON company_profiles.user_id = users.id").
Where("users.registration_status = ?", userRegStatus).
Preload("User").
Preload("User.Role").
Find(&companyProfiles).Error; err != nil {
log.Printf("Error fetching company profiles by user registration status: %v", err)
return nil, fmt.Errorf("error fetching company profiles by user registration status: %w", err)
}
log.Printf("Found %d company profiles with registration status: %s", len(companyProfiles), userRegStatus)
return companyProfiles, nil
}

View File

@ -315,7 +315,9 @@ func (s *authenticationService) VerifyLoginOTP(ctx context.Context, req *VerifyO
return nil, fmt.Errorf("kode OTP salah") return nil, fmt.Errorf("kode OTP salah")
} }
user, err := s.authRepo.FindUserByID(ctx, otpData.UserID) normalizedRole := strings.ToLower(req.RoleName)
user, err := s.authRepo.FindUserByPhoneAndRole(ctx, req.Phone, normalizedRole)
if err != nil { if err != nil {
return nil, fmt.Errorf("user tidak ditemukan") return nil, fmt.Errorf("user tidak ditemukan")
} }
@ -324,24 +326,29 @@ func (s *authenticationService) VerifyLoginOTP(ctx context.Context, req *VerifyO
tokenResponse, err := utils.GenerateTokenPair( tokenResponse, err := utils.GenerateTokenPair(
user.ID, user.ID,
user.Role.RoleName, normalizedRole,
req.DeviceID, req.DeviceID,
"pin_verification_required", user.RegistrationStatus,
int(user.RegistrationProgress), int(user.RegistrationProgress),
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("gagal generate token: %v", err) return nil, fmt.Errorf("gagal generate token: %v", err)
} }
nextStep := utils.GetNextRegistrationStep(
normalizedRole,
int(user.RegistrationProgress),
user.RegistrationStatus,
)
return &AuthResponse{ return &AuthResponse{
Message: "OTP berhasil diverifikasi, silakan masukkan PIN", Message: "otp berhasil diverifikasi",
AccessToken: tokenResponse.AccessToken, AccessToken: tokenResponse.AccessToken,
RefreshToken: tokenResponse.RefreshToken, RefreshToken: tokenResponse.RefreshToken,
TokenType: string(tokenResponse.TokenType), TokenType: string(tokenResponse.TokenType),
ExpiresIn: tokenResponse.ExpiresIn, ExpiresIn: tokenResponse.ExpiresIn,
User: convertUserToResponse(user),
RegistrationStatus: user.RegistrationStatus, RegistrationStatus: user.RegistrationStatus,
NextStep: "Masukkan PIN", NextStep: nextStep,
SessionID: tokenResponse.SessionID, SessionID: tokenResponse.SessionID,
}, nil }, nil
} }

View File

@ -2,6 +2,7 @@ package company
import ( import (
"rijig/config" "rijig/config"
"rijig/internal/authentication"
"rijig/middleware" "rijig/middleware"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -9,7 +10,8 @@ import (
func CompanyRouter(api fiber.Router) { func CompanyRouter(api fiber.Router) {
companyProfileRepo := NewCompanyProfileRepository(config.DB) companyProfileRepo := NewCompanyProfileRepository(config.DB)
companyProfileService := NewCompanyProfileService(companyProfileRepo) authRepo := authentication.NewAuthenticationRepository(config.DB)
companyProfileService := NewCompanyProfileService(companyProfileRepo, authRepo)
companyProfileHandler := NewCompanyProfileHandler(companyProfileService) companyProfileHandler := NewCompanyProfileHandler(companyProfileService)
companyProfileAPI := api.Group("/companyprofile") companyProfileAPI := api.Group("/companyprofile")

View File

@ -3,8 +3,13 @@ package company
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"rijig/internal/authentication"
"rijig/internal/role"
"rijig/internal/userprofile"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
"time"
) )
type CompanyProfileService interface { type CompanyProfileService interface {
@ -13,15 +18,19 @@ type CompanyProfileService interface {
GetCompanyProfilesByUserID(ctx context.Context, userID string) ([]ResponseCompanyProfileDTO, error) GetCompanyProfilesByUserID(ctx context.Context, userID string) ([]ResponseCompanyProfileDTO, error)
UpdateCompanyProfile(ctx context.Context, userID string, request *RequestCompanyProfileDTO) (*ResponseCompanyProfileDTO, error) UpdateCompanyProfile(ctx context.Context, userID string, request *RequestCompanyProfileDTO) (*ResponseCompanyProfileDTO, error)
DeleteCompanyProfile(ctx context.Context, userID string) error DeleteCompanyProfile(ctx context.Context, userID string) error
GetAllCompanyProfilesByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseCompanyProfileDTO, error)
UpdateUserRegistrationStatusByCompany(ctx context.Context, companyUserID string, newStatus string) error
} }
type companyProfileService struct { type companyProfileService struct {
companyRepo CompanyProfileRepository companyRepo CompanyProfileRepository
authRepo authentication.AuthenticationRepository
} }
func NewCompanyProfileService(companyRepo CompanyProfileRepository) CompanyProfileService { func NewCompanyProfileService(companyRepo CompanyProfileRepository, authRepo authentication.AuthenticationRepository) CompanyProfileService {
return &companyProfileService{ return &companyProfileService{
companyRepo: companyRepo, companyRepo, authRepo,
} }
} }
@ -48,9 +57,9 @@ func FormatResponseCompanyProfile(companyProfile *model.CompanyProfile) (*Respon
} }
func (s *companyProfileService) CreateCompanyProfile(ctx context.Context, userID string, request *RequestCompanyProfileDTO) (*ResponseCompanyProfileDTO, error) { func (s *companyProfileService) CreateCompanyProfile(ctx context.Context, userID string, request *RequestCompanyProfileDTO) (*ResponseCompanyProfileDTO, error) {
if errors, valid := request.ValidateCompanyProfileInput(); !valid { // if errors, valid := request.ValidateCompanyProfileInput(); !valid {
return nil, fmt.Errorf("validation failed: %v", errors) // return nil, fmt.Errorf("validation failed: %v", errors)
} // }
companyProfile := &model.CompanyProfile{ companyProfile := &model.CompanyProfile{
UserID: userID, UserID: userID,
@ -134,3 +143,100 @@ func (s *companyProfileService) UpdateCompanyProfile(ctx context.Context, userID
func (s *companyProfileService) DeleteCompanyProfile(ctx context.Context, userID string) error { func (s *companyProfileService) DeleteCompanyProfile(ctx context.Context, userID string) error {
return s.companyRepo.DeleteCompanyProfileByUserID(ctx, userID) return s.companyRepo.DeleteCompanyProfileByUserID(ctx, userID)
} }
func (s *companyProfileService) GetAllCompanyProfilesByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseCompanyProfileDTO, error) {
companyProfiles, err := s.authRepo.GetCompanyProfilesByUserRegStatus(ctx, userRegStatus)
if err != nil {
log.Printf("Error getting company profiles by registration status: %v", err)
return nil, fmt.Errorf("failed to get company profiles: %w", err)
}
var response []ResponseCompanyProfileDTO
for _, profile := range companyProfiles {
dto := ResponseCompanyProfileDTO{
ID: profile.ID,
UserID: profile.UserID,
CompanyName: profile.CompanyName,
CompanyAddress: profile.CompanyAddress,
CompanyPhone: profile.CompanyPhone,
CompanyEmail: profile.CompanyEmail,
CompanyLogo: profile.CompanyLogo,
CompanyWebsite: profile.CompanyWebsite,
TaxID: profile.TaxID,
FoundedDate: profile.FoundedDate,
CompanyType: profile.CompanyType,
CompanyDescription: profile.CompanyDescription,
CreatedAt: profile.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: profile.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
}
response = append(response, dto)
}
return response, nil
}
func (s *companyProfileService) UpdateUserRegistrationStatusByCompany(ctx context.Context, companyUserID string, newStatus string) error {
user, err := s.authRepo.FindUserByID(ctx, companyUserID)
if err != nil {
log.Printf("Error finding user by ID %s: %v", companyUserID, err)
return fmt.Errorf("user not found: %w", err)
}
updates := map[string]interface{}{
"registration_status": newStatus,
"updated_at": time.Now(),
}
switch newStatus {
case utils.RegStatusConfirmed:
updates["registration_progress"] = utils.ProgressDataSubmitted
case utils.RegStatusRejected:
updates["registration_progress"] = utils.ProgressOTPVerified
}
err = s.authRepo.PatchUser(ctx, user.ID, updates)
if err != nil {
log.Printf("Error updating user registration status for user ID %s: %v", user.ID, err)
return fmt.Errorf("failed to update user registration status: %w", err)
}
log.Printf("Successfully updated registration status for user ID %s to %s", user.ID, newStatus)
return nil
}
func (s *companyProfileService) GetUserProfile(ctx context.Context, userID string) (*userprofile.UserProfileResponseDTO, error) {
user, err := s.authRepo.FindUserByID(ctx, userID)
if err != nil {
log.Printf("Error getting user profile for ID %s: %v", userID, err)
return nil, fmt.Errorf("failed to get user profile: %w", err)
}
response := &userprofile.UserProfileResponseDTO{
ID: user.ID,
Name: user.Name,
Gender: user.Gender,
Dateofbirth: user.Dateofbirth,
Placeofbirth: user.Placeofbirth,
Phone: user.Phone,
Email: user.Email,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: user.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
}
if user.Avatar != nil {
response.Avatar = *user.Avatar
}
if user.Role != nil {
response.Role = role.RoleResponseDTO{
ID: user.Role.ID,
RoleName: user.Role.RoleName,
CreatedAt: user.Role.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: user.Role.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
}
}
return response, nil
}

View File

@ -9,6 +9,7 @@ type ResponseIdentityCardDTO struct {
ID string `json:"id"` ID string `json:"id"`
UserID string `json:"userId"` UserID string `json:"userId"`
Identificationumber string `json:"identificationumber"` Identificationumber string `json:"identificationumber"`
Fullname string `json:"fullname"`
Placeofbirth string `json:"placeofbirth"` Placeofbirth string `json:"placeofbirth"`
Dateofbirth string `json:"dateofbirth"` Dateofbirth string `json:"dateofbirth"`
Gender string `json:"gender"` Gender string `json:"gender"`
@ -31,9 +32,10 @@ type ResponseIdentityCardDTO struct {
} }
type RequestIdentityCardDTO struct { type RequestIdentityCardDTO struct {
DeviceID string `json:"device_id"` // DeviceID string `json:"device_id"`
UserID string `json:"userId"` UserID string `json:"userId"`
Identificationumber string `json:"identificationumber"` Identificationumber string `json:"identificationumber"`
Fullname string `json:"fullname"`
Placeofbirth string `json:"placeofbirth"` Placeofbirth string `json:"placeofbirth"`
Dateofbirth string `json:"dateofbirth"` Dateofbirth string `json:"dateofbirth"`
Gender string `json:"gender"` Gender string `json:"gender"`

View File

@ -1,8 +1,10 @@
package identitycart package identitycart
import ( import (
"log"
"rijig/middleware" "rijig/middleware"
"rijig/utils" "rijig/utils"
"strings"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -18,27 +20,33 @@ func NewIdentityCardHandler(service IdentityCardService) *IdentityCardHandler {
func (h *IdentityCardHandler) CreateIdentityCardHandler(c *fiber.Ctx) error { func (h *IdentityCardHandler) CreateIdentityCardHandler(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c) claims, err := middleware.GetUserFromContext(c)
if err != nil { if err != nil {
return err log.Printf("Error getting user from context: %v", err)
} return utils.Unauthorized(c, "unauthorized access")
cardPhoto, err := c.FormFile("cardphoto")
if err != nil {
return utils.BadRequest(c, "KTP photo is required")
} }
var input RequestIdentityCardDTO var input RequestIdentityCardDTO
if err := c.BodyParser(&input); err != nil { if err := c.BodyParser(&input); err != nil {
log.Printf("Error parsing body: %v", err)
return utils.BadRequest(c, "Invalid input format") return utils.BadRequest(c, "Invalid input format")
} }
if errs, valid := input.ValidateIdentityCardInput(); !valid { if errs, valid := input.ValidateIdentityCardInput(); !valid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Input validation failed", errs) return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Input validation failed", errs)
} }
response, err := h.service.CreateIdentityCard(c.Context(), claims.UserID, &input, cardPhoto) cardPhoto, err := c.FormFile("cardphoto")
if err != nil { if err != nil {
return utils.InternalServerError(c, err.Error()) log.Printf("Error getting card photo: %v", err)
return utils.BadRequest(c, "KTP photo is required")
}
response, err := h.service.CreateIdentityCard(c.Context(), claims.UserID, claims.DeviceID, &input, cardPhoto)
if err != nil {
log.Printf("Error creating identity card: %v", err)
if strings.Contains(err.Error(), "invalid file type") {
return utils.BadRequest(c, err.Error())
}
return utils.InternalServerError(c, "Failed to create identity card")
} }
return utils.SuccessWithData(c, "KTP successfully submitted", response) return utils.SuccessWithData(c, "KTP successfully submitted", response)
@ -47,27 +55,158 @@ func (h *IdentityCardHandler) CreateIdentityCardHandler(c *fiber.Ctx) error {
func (h *IdentityCardHandler) GetIdentityByID(c *fiber.Ctx) error { func (h *IdentityCardHandler) GetIdentityByID(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
if id == "" { if id == "" {
return utils.BadRequest(c, "id is required") return utils.BadRequest(c, "ID is required")
} }
result, err := h.service.GetIdentityCardByID(c.Context(), id) result, err := h.service.GetIdentityCardByID(c.Context(), id)
if err != nil { if err != nil {
return utils.NotFound(c, "data not found") log.Printf("Error getting identity card by ID %s: %v", id, err)
return utils.NotFound(c, "Identity card not found")
} }
return utils.SuccessWithData(c, "success retrieve identity card", result) return utils.SuccessWithData(c, "Successfully retrieved identity card", result)
} }
func (h *IdentityCardHandler) GetIdentityByUserId(c *fiber.Ctx) error { func (h *IdentityCardHandler) GetIdentityByUserId(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c) claims, err := middleware.GetUserFromContext(c)
if err != nil { if err != nil {
return err log.Printf("Error getting user from context: %v", err)
return utils.Unauthorized(c, "Unauthorized access")
} }
result, err := h.service.GetIdentityCardsByUserID(c.Context(), claims.UserID) result, err := h.service.GetIdentityCardsByUserID(c.Context(), claims.UserID)
if err != nil { if err != nil {
return utils.InternalServerError(c, "failed to fetch your identity card data") log.Printf("Error getting identity cards for user %s: %v", claims.UserID, err)
return utils.InternalServerError(c, "Failed to fetch your identity card data")
} }
return utils.SuccessWithData(c, "success retrieve your identity card", result) return utils.SuccessWithData(c, "Successfully retrieved your identity cards", result)
}
func (h *IdentityCardHandler) UpdateIdentityCardHandler(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
log.Printf("Error getting user from context: %v", err)
return utils.Unauthorized(c, "Unauthorized access")
}
id := c.Params("id")
if id == "" {
return utils.BadRequest(c, "Identity card ID is required")
}
var input RequestIdentityCardDTO
if err := c.BodyParser(&input); err != nil {
log.Printf("Error parsing body: %v", err)
return utils.BadRequest(c, "Invalid input format")
}
if errs, valid := input.ValidateIdentityCardInput(); !valid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Input validation failed", errs)
}
cardPhoto, err := c.FormFile("cardphoto")
if err != nil && err.Error() != "there is no uploaded file associated with the given key" {
log.Printf("Error getting card photo: %v", err)
return utils.BadRequest(c, "Invalid card photo")
}
if cardPhoto != nil && cardPhoto.Size > 5*1024*1024 {
return utils.BadRequest(c, "File size must be less than 5MB")
}
response, err := h.service.UpdateIdentityCard(c.Context(), claims.UserID, id, &input, cardPhoto)
if err != nil {
log.Printf("Error updating identity card: %v", err)
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Identity card not found")
}
if strings.Contains(err.Error(), "invalid file type") {
return utils.BadRequest(c, err.Error())
}
return utils.InternalServerError(c, "Failed to update identity card")
}
return utils.SuccessWithData(c, "Identity card successfully updated", response)
}
func (h *IdentityCardHandler) GetAllIdentityCardsByRegStatus(c *fiber.Ctx) error {
_, err := middleware.GetUserFromContext(c)
if err != nil {
log.Printf("Error getting user from context: %v", err)
return utils.Unauthorized(c, "Unauthorized access")
}
// if claims.Role != "admin" {
// return utils.Forbidden(c, "Access denied: admin role required")
// }
status := c.Query("status", utils.RegStatusPending)
validStatuses := map[string]bool{
utils.RegStatusPending: true,
"confirmed": true,
"rejected": true,
}
if !validStatuses[status] {
return utils.BadRequest(c, "Invalid status. Valid values: pending, confirmed, rejected")
}
result, err := h.service.GetAllIdentityCardsByRegStatus(c.Context(), status)
if err != nil {
log.Printf("Error getting identity cards by status %s: %v", status, err)
return utils.InternalServerError(c, "Failed to fetch identity cards")
}
return utils.SuccessWithData(c, "Successfully retrieved identity cards", result)
}
func (h *IdentityCardHandler) UpdateUserRegistrationStatusByIdentityCard(c *fiber.Ctx) error {
_, err := middleware.GetUserFromContext(c)
if err != nil {
log.Printf("Error getting user from context: %v", err)
return utils.Unauthorized(c, "Unauthorized access")
}
userID := c.Params("userId")
if userID == "" {
return utils.BadRequest(c, "User ID is required")
}
type StatusUpdateRequest struct {
Status string `json:"status" validate:"required,oneof=confirmed rejected"`
}
var input StatusUpdateRequest
if err := c.BodyParser(&input); err != nil {
log.Printf("Error parsing body: %v", err)
return utils.BadRequest(c, "Invalid input format")
}
if input.Status != "confirmed" && input.Status != "rejected" {
return utils.BadRequest(c, "Invalid status. Valid values: confirmed, rejected")
}
err = h.service.UpdateUserRegistrationStatusByIdentityCard(c.Context(), userID, input.Status)
if err != nil {
log.Printf("Error updating user registration status: %v", err)
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "User not found")
}
return utils.InternalServerError(c, "Failed to update registration status")
}
message := "User registration status successfully updated to " + input.Status
return utils.Success(c, message)
}
func (h *IdentityCardHandler) DeleteIdentityCardHandler(c *fiber.Ctx) error {
id := c.Params("id")
if id == "" {
return utils.BadRequest(c, "Identity card ID is required")
}
return utils.Success(c, "Identity card successfully deleted")
} }

View File

@ -3,7 +3,9 @@ package identitycart
import ( import (
"rijig/config" "rijig/config"
"rijig/internal/authentication" "rijig/internal/authentication"
"rijig/internal/userprofile"
"rijig/middleware" "rijig/middleware"
"rijig/utils"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -11,25 +13,34 @@ import (
func UserIdentityCardRoute(api fiber.Router) { func UserIdentityCardRoute(api fiber.Router) {
identityRepo := NewIdentityCardRepository(config.DB) identityRepo := NewIdentityCardRepository(config.DB)
authRepo := authentication.NewAuthenticationRepository(config.DB) authRepo := authentication.NewAuthenticationRepository(config.DB)
identityService := NewIdentityCardService(identityRepo, authRepo) userRepo := userprofile.NewUserProfileRepository(config.DB)
identityService := NewIdentityCardService(identityRepo, authRepo, userRepo)
identityHandler := NewIdentityCardHandler(identityService) identityHandler := NewIdentityCardHandler(identityService)
identity := api.Group("/identity") identity := api.Group("/identity")
identity.Post("/create", identity.Post("/create",
middleware.AuthMiddleware(), middleware.AuthMiddleware(),
middleware.RequireRoles("pengelola", "pengepul"), middleware.RequireRoles(utils.RolePengepul),
identityHandler.CreateIdentityCardHandler, identityHandler.CreateIdentityCardHandler,
) )
identity.Get("/:id", identity.Get("/:id",
middleware.AuthMiddleware(), middleware.AuthMiddleware(),
middleware.RequireRoles("pengelola", "pengepul"),
identityHandler.GetIdentityByID, identityHandler.GetIdentityByID,
) )
identity.Get("/s",
middleware.AuthMiddleware(),
identityHandler.GetIdentityByUserId,
)
identity.Get("/", identity.Get("/",
middleware.AuthMiddleware(), middleware.AuthMiddleware(),
middleware.RequireRoles("pengelola", "pengepul"), middleware.RequireRoles(utils.RoleAdministrator),
identityHandler.GetIdentityByUserId, identityHandler.GetAllIdentityCardsByRegStatus,
)
identity.Patch("/:userId/status",
middleware.AuthMiddleware(),
middleware.RequireRoles(utils.RoleAdministrator),
identityHandler.UpdateUserRegistrationStatusByIdentityCard,
) )
} }

View File

@ -2,36 +2,49 @@ package identitycart
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io"
"log" "log"
"mime/multipart" "mime/multipart"
"os" "os"
"path/filepath" "path/filepath"
"rijig/internal/authentication" "rijig/internal/authentication"
"rijig/internal/role"
"rijig/internal/userprofile"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
"strings" "time"
) )
type IdentityCardService interface { type IdentityCardService interface {
CreateIdentityCard(ctx context.Context, userID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error) CreateIdentityCard(ctx context.Context, userID, deviceID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error)
GetIdentityCardByID(ctx context.Context, id string) (*ResponseIdentityCardDTO, error) GetIdentityCardByID(ctx context.Context, id string) (*ResponseIdentityCardDTO, error)
GetIdentityCardsByUserID(ctx context.Context, userID string) ([]ResponseIdentityCardDTO, error) GetIdentityCardsByUserID(ctx context.Context, userID string) ([]ResponseIdentityCardDTO, error)
UpdateIdentityCard(ctx context.Context, userID string, id string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*ResponseIdentityCardDTO, error) UpdateIdentityCard(ctx context.Context, userID string, id string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*ResponseIdentityCardDTO, error)
GetAllIdentityCardsByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseIdentityCardDTO, error)
UpdateUserRegistrationStatusByIdentityCard(ctx context.Context, identityCardUserID string, newStatus string) error
} }
type identityCardService struct { type identityCardService struct {
identityRepo IdentityCardRepository identityRepo IdentityCardRepository
authRepo authentication.AuthenticationRepository authRepo authentication.AuthenticationRepository
userRepo userprofile.UserProfileRepository
} }
func NewIdentityCardService(identityRepo IdentityCardRepository, authRepo authentication.AuthenticationRepository) IdentityCardService { func NewIdentityCardService(identityRepo IdentityCardRepository, authRepo authentication.AuthenticationRepository, userRepo userprofile.UserProfileRepository) IdentityCardService {
return &identityCardService{ return &identityCardService{
identityRepo: identityRepo, identityRepo,
authRepo: authRepo, authRepo, userRepo,
} }
} }
type IdentityCardWithUserDTO struct {
IdentityCard ResponseIdentityCardDTO `json:"identity_card"`
User userprofile.UserProfileResponseDTO `json:"user"`
}
func FormatResponseIdentityCard(identityCard *model.IdentityCard) (*ResponseIdentityCardDTO, error) { func FormatResponseIdentityCard(identityCard *model.IdentityCard) (*ResponseIdentityCardDTO, error) {
createdAt, _ := utils.FormatDateToIndonesianFormat(identityCard.CreatedAt) createdAt, _ := utils.FormatDateToIndonesianFormat(identityCard.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(identityCard.UpdatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(identityCard.UpdatedAt)
@ -93,7 +106,7 @@ func (s *identityCardService) saveIdentityCardImage(userID string, cardPhoto *mu
} }
defer dst.Close() defer dst.Close()
if _, err := dst.ReadFrom(src); err != nil { if _, err := io.Copy(dst, src); err != nil {
return "", fmt.Errorf("failed to save card photo: %v", err) return "", fmt.Errorf("failed to save card photo: %v", err)
} }
@ -123,16 +136,7 @@ func deleteIdentityCardImage(imagePath string) error {
return nil return nil
} }
func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error) { func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID, deviceID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error) {
// Validate essential parameters
if userID == "" {
return nil, fmt.Errorf("userID cannot be empty")
}
if request.DeviceID == "" {
return nil, fmt.Errorf("deviceID cannot be empty")
}
cardPhotoPath, err := s.saveIdentityCardImage(userID, cardPhoto) cardPhotoPath, err := s.saveIdentityCardImage(userID, cardPhoto)
if err != nil { if err != nil {
@ -172,33 +176,13 @@ func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID str
return nil, fmt.Errorf("failed to find user: %v", err) return nil, fmt.Errorf("failed to find user: %v", err)
} }
// Validate user data
if user.Role.RoleName == "" { if user.Role.RoleName == "" {
return nil, fmt.Errorf("user role not found") return nil, fmt.Errorf("user role not found")
} }
roleName := strings.ToLower(user.Role.RoleName)
// Determine new registration status and progress
var newRegistrationStatus string
var newRegistrationProgress int
switch roleName {
case "pengepul":
newRegistrationProgress = 2
newRegistrationStatus = utils.RegStatusPending
case "pengelola":
newRegistrationProgress = 2
newRegistrationStatus = user.RegistrationStatus
default:
newRegistrationProgress = int(user.RegistrationProgress)
newRegistrationStatus = user.RegistrationStatus
}
// Update user registration progress and status
updates := map[string]interface{}{ updates := map[string]interface{}{
"registration_progress": newRegistrationProgress, "registration_progress": utils.ProgressDataSubmitted,
"registration_status": newRegistrationStatus, "registration_status": utils.RegStatusPending,
} }
err = s.authRepo.PatchUser(ctx, userID, updates) err = s.authRepo.PatchUser(ctx, userID, updates)
@ -206,34 +190,46 @@ func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID str
return nil, fmt.Errorf("failed to update user: %v", err) return nil, fmt.Errorf("failed to update user: %v", err)
} }
// Debug logging before token generation updated, err := s.userRepo.GetByID(ctx, userID)
if err != nil {
if errors.Is(err, userprofile.ErrUserNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to get updated user: %w", err)
}
log.Printf("Token Generation Parameters:") log.Printf("Token Generation Parameters:")
log.Printf("- UserID: '%s'", user.ID) log.Printf("- UserID: '%s'", user.ID)
log.Printf("- Role: '%s'", user.Role.RoleName) log.Printf("- Role: '%s'", user.Role.RoleName)
log.Printf("- DeviceID: '%s'", request.DeviceID) log.Printf("- DeviceID: '%s'", deviceID)
log.Printf("- Registration Status: '%s'", newRegistrationStatus) log.Printf("- Registration Status: '%s'", utils.RegStatusPending)
// Generate token pair with updated status
tokenResponse, err := utils.GenerateTokenPair( tokenResponse, err := utils.GenerateTokenPair(
user.ID, updated.ID,
user.Role.RoleName, updated.Role.RoleName,
request.DeviceID, deviceID,
newRegistrationStatus, updated.RegistrationStatus,
newRegistrationProgress, int(updated.RegistrationProgress),
) )
if err != nil { if err != nil {
log.Printf("GenerateTokenPair error: %v", err) log.Printf("GenerateTokenPair error: %v", err)
return nil, fmt.Errorf("failed to generate token: %v", err) return nil, fmt.Errorf("failed to generate token: %v", err)
} }
nextStep := utils.GetNextRegistrationStep(
updated.Role.RoleName,
int(updated.RegistrationProgress),
updated.RegistrationStatus,
)
return &authentication.AuthResponse{ return &authentication.AuthResponse{
Message: "identity card berhasil diunggah, silakan tunggu konfirmasi dari admin dalam 1x24 jam", Message: "identity card berhasil diunggah, silakan tunggu konfirmasi dari admin dalam 1x24 jam",
AccessToken: tokenResponse.AccessToken, AccessToken: tokenResponse.AccessToken,
RefreshToken: tokenResponse.RefreshToken, RefreshToken: tokenResponse.RefreshToken,
TokenType: string(tokenResponse.TokenType), TokenType: string(tokenResponse.TokenType),
ExpiresIn: tokenResponse.ExpiresIn, ExpiresIn: tokenResponse.ExpiresIn,
RegistrationStatus: newRegistrationStatus, RegistrationStatus: updated.RegistrationStatus,
NextStep: tokenResponse.NextStep, NextStep: nextStep,
SessionID: tokenResponse.SessionID, SessionID: tokenResponse.SessionID,
}, nil }, nil
} }
@ -319,3 +315,164 @@ func (s *identityCardService) UpdateIdentityCard(ctx context.Context, userID str
return idcardResponseDTO, nil return idcardResponseDTO, nil
} }
func (s *identityCardService) GetAllIdentityCardsByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseIdentityCardDTO, error) {
identityCards, err := s.authRepo.GetIdentityCardsByUserRegStatus(ctx, userRegStatus)
if err != nil {
log.Printf("Error getting identity cards by registration status: %v", err)
return nil, fmt.Errorf("failed to get identity cards: %w", err)
}
var response []ResponseIdentityCardDTO
for _, card := range identityCards {
createdAt, _ := utils.FormatDateToIndonesianFormat(card.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(card.UpdatedAt)
dto := ResponseIdentityCardDTO{
ID: card.ID,
UserID: card.UserID,
Identificationumber: card.Identificationumber,
Placeofbirth: card.Placeofbirth,
Dateofbirth: card.Dateofbirth,
Gender: card.Gender,
BloodType: card.BloodType,
Province: card.Province,
District: card.District,
SubDistrict: card.SubDistrict,
Hamlet: card.Hamlet,
Village: card.Village,
Neighbourhood: card.Neighbourhood,
PostalCode: card.PostalCode,
Religion: card.Religion,
Maritalstatus: card.Maritalstatus,
Job: card.Job,
Citizenship: card.Citizenship,
Validuntil: card.Validuntil,
Cardphoto: card.Cardphoto,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
response = append(response, dto)
}
return response, nil
}
func (s *identityCardService) UpdateUserRegistrationStatusByIdentityCard(ctx context.Context, identityCardUserID string, newStatus string) error {
user, err := s.authRepo.FindUserByID(ctx, identityCardUserID)
if err != nil {
log.Printf("Error finding user by ID %s: %v", identityCardUserID, err)
return fmt.Errorf("user not found: %w", err)
}
updates := map[string]interface{}{
"registration_status": newStatus,
"updated_at": time.Now(),
}
switch newStatus {
case utils.RegStatusConfirmed:
updates["registration_progress"] = utils.ProgressDataSubmitted
identityCards, err := s.GetIdentityCardsByUserID(ctx, identityCardUserID)
if err != nil {
log.Printf("Error fetching identity cards for user ID %s: %v", identityCardUserID, err)
return fmt.Errorf("failed to fetch identity card data: %w", err)
}
if len(identityCards) == 0 {
log.Printf("No identity card found for user ID %s", identityCardUserID)
return fmt.Errorf("no identity card found for user")
}
identityCard := identityCards[0]
updates["name"] = identityCard.Fullname
updates["gender"] = identityCard.Gender
updates["dateofbirth"] = identityCard.Dateofbirth
updates["placeofbirth"] = identityCard.District
log.Printf("Syncing user data for ID %s: name=%s, gender=%s, dob=%s, pob=%s",
identityCardUserID, identityCard.Fullname, identityCard.Gender,
identityCard.Dateofbirth, identityCard.District)
case utils.RegStatusRejected:
updates["registration_progress"] = utils.ProgressOTPVerified
}
err = s.authRepo.PatchUser(ctx, user.ID, updates)
if err != nil {
log.Printf("Error updating user registration status for user ID %s: %v", user.ID, err)
return fmt.Errorf("failed to update user registration status: %w", err)
}
log.Printf("Successfully updated registration status for user ID %s to %s", user.ID, newStatus)
if newStatus == utils.RegStatusConfirmed {
log.Printf("User profile data synced successfully for user ID %s", user.ID)
}
return nil
}
func (s *identityCardService) mapIdentityCardToDTO(card model.IdentityCard) ResponseIdentityCardDTO {
createdAt, _ := utils.FormatDateToIndonesianFormat(card.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(card.UpdatedAt)
return ResponseIdentityCardDTO{
ID: card.ID,
UserID: card.UserID,
Identificationumber: card.Identificationumber,
Placeofbirth: card.Placeofbirth,
Dateofbirth: card.Dateofbirth,
Gender: card.Gender,
BloodType: card.BloodType,
Province: card.Province,
District: card.District,
SubDistrict: card.SubDistrict,
Hamlet: card.Hamlet,
Village: card.Village,
Neighbourhood: card.Neighbourhood,
PostalCode: card.PostalCode,
Religion: card.Religion,
Maritalstatus: card.Maritalstatus,
Job: card.Job,
Citizenship: card.Citizenship,
Validuntil: card.Validuntil,
Cardphoto: card.Cardphoto,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
}
func (s *identityCardService) mapUserToDTO(user model.User) userprofile.UserProfileResponseDTO {
avatar := ""
if user.Avatar != nil {
avatar = *user.Avatar
}
var roleDTO role.RoleResponseDTO
if user.Role != nil {
roleDTO = role.RoleResponseDTO{
ID: user.Role.ID,
RoleName: user.Role.RoleName,
}
}
createdAt, _ := utils.FormatDateToIndonesianFormat(user.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(user.UpdatedAt)
return userprofile.UserProfileResponseDTO{
ID: user.ID,
Avatar: avatar,
Name: user.Name,
Gender: user.Gender,
Dateofbirth: user.Dateofbirth,
Placeofbirth: user.Placeofbirth,
Phone: user.Phone,
Email: user.Email,
PhoneVerified: user.PhoneVerified,
Role: roleDTO,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
}

View File

@ -6,7 +6,7 @@ import (
) )
type RequestPinDTO struct { type RequestPinDTO struct {
DeviceId string `json:"device_id"` // DeviceId string `json:"device_id"`
Pin string `json:"userpin"` Pin string `json:"userpin"`
} }

View File

@ -15,47 +15,39 @@ func NewUserPinHandler(service UserPinService) *UserPinHandler {
return &UserPinHandler{service} return &UserPinHandler{service}
} }
// userID, ok := c.Locals("user_id").(string)
//
// if !ok || userID == "" {
// return utils.Unauthorized(c, "user_id is missing or invalid")
// }
func (h *UserPinHandler) CreateUserPinHandler(c *fiber.Ctx) error { func (h *UserPinHandler) CreateUserPinHandler(c *fiber.Ctx) error {
// Ambil klaim pengguna yang sudah diautentikasi
claims, err := middleware.GetUserFromContext(c) claims, err := middleware.GetUserFromContext(c)
if err != nil { if err != nil {
return err return utils.Unauthorized(c, "Authentication required")
}
if claims.UserID == "" || claims.DeviceID == "" {
return utils.BadRequest(c, "Invalid user claims")
} }
// Parsing body request untuk PIN
var req RequestPinDTO var req RequestPinDTO
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
return utils.BadRequest(c, "Invalid request body") return utils.BadRequest(c, "Invalid request body")
} }
// Validasi request PIN
if errs, ok := req.ValidateRequestPinDTO(); !ok { if errs, ok := req.ValidateRequestPinDTO(); !ok {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation error", errs) return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation error", errs)
} }
// Panggil service untuk membuat PIN pintokenresponse, err := h.service.CreateUserPin(c.Context(), claims.UserID, claims.DeviceID, &req)
err = h.service.CreateUserPin(c.Context(), claims.UserID, &req)
if err != nil { if err != nil {
if err.Error() == "PIN already created" { if err.Error() == Pinhasbeencreated {
return utils.BadRequest(c, err.Error()) // Jika PIN sudah ada, kembalikan error 400 return utils.BadRequest(c, err.Error())
} }
return utils.InternalServerError(c, err.Error()) // Jika terjadi error lain, internal server error return utils.InternalServerError(c, err.Error())
} }
// Mengembalikan response sukses jika berhasil return utils.SuccessWithData(c, "PIN created successfully", pintokenresponse)
return utils.Success(c, "PIN created successfully")
} }
func (h *UserPinHandler) VerifyPinHandler(c *fiber.Ctx) error { func (h *UserPinHandler) VerifyPinHandler(c *fiber.Ctx) error {
// userID, ok := c.Locals("user_id").(string)
// if !ok || userID == "" {
// return utils.Unauthorized(c, "user_id is missing or invalid")
// }
claims, err := middleware.GetUserFromContext(c) claims, err := middleware.GetUserFromContext(c)
if err != nil { if err != nil {
return err return err
@ -66,12 +58,10 @@ func (h *UserPinHandler) VerifyPinHandler(c *fiber.Ctx) error {
return utils.BadRequest(c, "Invalid request body") return utils.BadRequest(c, "Invalid request body")
} }
token, err := h.service.VerifyUserPin(c.Context(), claims.UserID, &req) token, err := h.service.VerifyUserPin(c.Context(), claims.UserID, claims.DeviceID, &req)
if err != nil { if err != nil {
return utils.BadRequest(c, err.Error()) return utils.BadRequest(c, err.Error())
} }
return utils.SuccessWithData(c, "PIN verified successfully", fiber.Map{ return utils.SuccessWithData(c, "PIN verified successfully", token)
"token": token,
})
} }

View File

@ -3,6 +3,7 @@ package userpin
import ( import (
"rijig/config" "rijig/config"
"rijig/internal/authentication" "rijig/internal/authentication"
"rijig/internal/userprofile"
"rijig/middleware" "rijig/middleware"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -11,8 +12,9 @@ import (
func UsersPinRoute(api fiber.Router) { func UsersPinRoute(api fiber.Router) {
userPinRepo := NewUserPinRepository(config.DB) userPinRepo := NewUserPinRepository(config.DB)
authRepo := authentication.NewAuthenticationRepository(config.DB) authRepo := authentication.NewAuthenticationRepository(config.DB)
userprofileRepo := userprofile.NewUserProfileRepository(config.DB)
userPinService := NewUserPinService(userPinRepo, authRepo) userPinService := NewUserPinService(userPinRepo, authRepo, userprofileRepo)
userPinHandler := NewUserPinHandler(userPinService) userPinHandler := NewUserPinHandler(userPinService)

View File

@ -2,45 +2,51 @@ package userpin
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"rijig/internal/authentication" "rijig/internal/authentication"
"rijig/internal/userprofile"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
"strings" "strings"
"gorm.io/gorm"
) )
type UserPinService interface { type UserPinService interface {
CreateUserPin(ctx context.Context, userID string, dto *RequestPinDTO) error CreateUserPin(ctx context.Context, userID, deviceId string, dto *RequestPinDTO) (*authentication.AuthResponse, error)
VerifyUserPin(ctx context.Context, userID string, pin *RequestPinDTO) (*utils.TokenResponse, error) VerifyUserPin(ctx context.Context, userID, deviceID string, pin *RequestPinDTO) (*utils.TokenResponse, error)
} }
type userPinService struct { type userPinService struct {
UserPinRepo UserPinRepository UserPinRepo UserPinRepository
authRepo authentication.AuthenticationRepository authRepo authentication.AuthenticationRepository
userProfileRepo userprofile.UserProfileRepository
} }
func NewUserPinService(UserPinRepo UserPinRepository, func NewUserPinService(UserPinRepo UserPinRepository,
authRepo authentication.AuthenticationRepository) UserPinService { authRepo authentication.AuthenticationRepository,
return &userPinService{UserPinRepo, authRepo} userProfileRepo userprofile.UserProfileRepository) UserPinService {
return &userPinService{UserPinRepo, authRepo, userProfileRepo}
} }
func (s *userPinService) CreateUserPin(ctx context.Context, userID string, dto *RequestPinDTO) error { var (
Pinhasbeencreated = "PIN already created"
)
if errs, ok := dto.ValidateRequestPinDTO(); !ok { func (s *userPinService) CreateUserPin(ctx context.Context, userID, deviceId string, dto *RequestPinDTO) (*authentication.AuthResponse, error) {
return fmt.Errorf("validation error: %v", errs)
}
existingPin, err := s.UserPinRepo.FindByUserID(ctx, userID) _, err := s.UserPinRepo.FindByUserID(ctx, userID)
if err != nil { if err != nil {
return fmt.Errorf("failed to check existing PIN: %w", err) if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user not found")
} }
if existingPin != nil { return nil, fmt.Errorf("%v", Pinhasbeencreated)
return fmt.Errorf("PIN already created")
} }
hashed, err := utils.HashingPlainText(dto.Pin) hashed, err := utils.HashingPlainText(dto.Pin)
if err != nil { if err != nil {
return fmt.Errorf("failed to hash PIN: %w", err) return nil, fmt.Errorf("failed to hash PIN: %w", err)
} }
userPin := &model.UserPin{ userPin := &model.UserPin{
@ -49,35 +55,63 @@ func (s *userPinService) CreateUserPin(ctx context.Context, userID string, dto *
} }
if err := s.UserPinRepo.Create(ctx, userPin); err != nil { if err := s.UserPinRepo.Create(ctx, userPin); err != nil {
return fmt.Errorf("failed to create PIN: %w", err) if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to create pin: %w", err)
} }
user, err := s.authRepo.FindUserByID(ctx, userID) updates := map[string]interface{}{
if err != nil { "registration_progress": utils.ProgressComplete,
return fmt.Errorf("user not found")
}
roleName := strings.ToLower(user.Role.RoleName)
progress := authentication.IsRegistrationComplete(roleName, int(user.RegistrationProgress))
// progress := utils.GetNextRegistrationStep(roleName, int(user.RegistrationProgress))
// progress := utils.GetNextRegistrationStep(roleName, user.RegistrationProgress)
// progress := utils.GetNextRegistrationStep(roleName, user.RegistrationProgress)
if !progress {
err = s.authRepo.PatchUser(ctx, userID, map[string]interface{}{
"registration_progress": int(user.RegistrationProgress) + 1,
"registration_status": utils.RegStatusComplete, "registration_status": utils.RegStatusComplete,
})
if err != nil {
return fmt.Errorf("failed to update user progress: %w", err)
}
} }
return nil if err = s.authRepo.PatchUser(ctx, userID, updates); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to update user profile: %w", err)
}
updated, err := s.userProfileRepo.GetByID(ctx, userID)
if err != nil {
if errors.Is(err, userprofile.ErrUserNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to get updated user: %w", err)
}
tokenResponse, err := utils.GenerateTokenPair(
updated.ID,
updated.Role.RoleName,
deviceId,
updated.RegistrationStatus,
int(updated.RegistrationProgress),
)
if err != nil {
return nil, fmt.Errorf("gagal generate token: %v", err)
}
nextStep := utils.GetNextRegistrationStep(
updated.Role.RoleName,
int(updated.RegistrationProgress),
updated.RegistrationStatus,
)
return &authentication.AuthResponse{
Message: "Isi data diri berhasil",
AccessToken: tokenResponse.AccessToken,
RefreshToken: tokenResponse.RefreshToken,
TokenType: string(tokenResponse.TokenType),
ExpiresIn: tokenResponse.ExpiresIn,
RegistrationStatus: updated.RegistrationStatus,
NextStep: nextStep,
SessionID: tokenResponse.SessionID,
}, nil
} }
func (s *userPinService) VerifyUserPin(ctx context.Context, userID string, pin *RequestPinDTO) (*utils.TokenResponse, error) { func (s *userPinService) VerifyUserPin(ctx context.Context, userID, deviceID string, pin *RequestPinDTO) (*utils.TokenResponse, error) {
user, err := s.authRepo.FindUserByID(ctx, userID) user, err := s.authRepo.FindUserByID(ctx, userID)
if err != nil { if err != nil {
return nil, fmt.Errorf("user not found") return nil, fmt.Errorf("user not found")
@ -93,5 +127,5 @@ func (s *userPinService) VerifyUserPin(ctx context.Context, userID string, pin *
} }
roleName := strings.ToLower(user.Role.RoleName) roleName := strings.ToLower(user.Role.RoleName)
return utils.GenerateTokenPair(user.ID, roleName, pin.DeviceId, user.RegistrationStatus, int(user.RegistrationProgress)) return utils.GenerateTokenPair(user.ID, roleName, deviceID, user.RegistrationStatus, int(user.RegistrationProgress))
} }

View File

@ -1 +1,76 @@
package userprofile package userprofile
import (
"context"
"log"
"rijig/middleware"
"rijig/utils"
"strings"
"time"
"github.com/gofiber/fiber/v2"
)
type UserProfileHandler struct {
service UserProfileService
}
func NewUserProfileHandler(service UserProfileService) *UserProfileHandler {
return &UserProfileHandler{
service: service,
}
}
func (h *UserProfileHandler) GetUserProfile(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
userProfile, err := h.service.GetUserProfile(ctx, claims.UserID)
if err != nil {
if strings.Contains(err.Error(), ErrUserNotFound.Error()) {
return utils.NotFound(c, "User profile not found")
}
log.Printf("Error getting user profile: %v", err)
return utils.InternalServerError(c, "Failed to retrieve user profile")
}
return utils.SuccessWithData(c, "User profile retrieved successfully", userProfile)
}
func (h *UserProfileHandler) UpdateUserProfile(c *fiber.Ctx) error {
claims, err := middleware.GetUserFromContext(c)
if err != nil {
return err
}
var req RequestUserProfileDTO
if err := c.BodyParser(&req); err != nil {
return utils.BadRequest(c, "Invalid request format")
}
if validationErrors, isValid := req.ValidateRequestUserProfileDTO(); !isValid {
return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation failed", validationErrors)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
updatedProfile, err := h.service.UpdateRegistUserProfile(ctx, claims.UserID, claims.DeviceID, &req)
if err != nil {
if strings.Contains(err.Error(), "user not found") {
return utils.NotFound(c, "User not found")
}
log.Printf("Error updating user profile: %v", err)
return utils.InternalServerError(c, "Failed to update user profile")
}
return utils.SuccessWithData(c, "User profile updated successfully", updatedProfile)
}

View File

@ -2,34 +2,106 @@ package userprofile
import ( import (
"context" "context"
"errors"
"rijig/model" "rijig/model"
"gorm.io/gorm" "gorm.io/gorm"
) )
type AuthenticationRepository interface { type UserProfileRepository interface {
UpdateUser(ctx context.Context, user *model.User) error GetByID(ctx context.Context, userID string) (*model.User, error)
PatchUser(ctx context.Context, userID string, updates map[string]interface{}) error GetByRoleName(ctx context.Context, roleName string) ([]*model.User, error)
// GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error)
// GetCompanyProfileByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error)
Update(ctx context.Context, userID string, user *model.User) error
} }
type authenticationRepository struct { type userProfileRepository struct {
db *gorm.DB db *gorm.DB
} }
func NewAuthenticationRepository(db *gorm.DB) AuthenticationRepository { func NewUserProfileRepository(db *gorm.DB) UserProfileRepository {
return &authenticationRepository{db} return &userProfileRepository{
db: db,
}
} }
func (r *authenticationRepository) UpdateUser(ctx context.Context, user *model.User) error { func (r *userProfileRepository) GetByID(ctx context.Context, userID string) (*model.User, error) {
return r.db.WithContext(ctx). var user model.User
Model(&model.User{}).
Where("id = ?", user.ID). err := r.db.WithContext(ctx).
Updates(user).Error Preload("Role").
Where("id = ?", userID).
First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUserNotFound
}
return nil, err
}
return &user, nil
} }
func (r *authenticationRepository) PatchUser(ctx context.Context, userID string, updates map[string]interface{}) error { func (r *userProfileRepository) GetByRoleName(ctx context.Context, roleName string) ([]*model.User, error) {
return r.db.WithContext(ctx). var users []*model.User
err := r.db.WithContext(ctx).
Preload("Role").
Joins("JOIN roles ON users.role_id = roles.id").
Where("roles.role_name = ?", roleName).
Find(&users).Error
if err != nil {
return nil, err
}
return users, nil
}
/* func (r *userProfileRepository) GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) {
var identityCards []model.IdentityCard
if err := r.db.WithContext(ctx).
Joins("JOIN users ON identity_cards.user_id = users.id").
Where("users.registration_status = ?", userRegStatus).
Preload("User").
Find(&identityCards).Error; err != nil {
log.Printf("Error fetching identity cards by user registration status: %v", err)
return nil, fmt.Errorf("error fetching identity cards by user registration status: %w", err)
}
return identityCards, nil
}
func (r *userProfileRepository) GetCompanyProfileByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) {
var identityCards []model.IdentityCard
if err := r.db.WithContext(ctx).
Joins("JOIN users ON company_profiles.user_id = users.id").
Where("users.registration_status = ?", userRegStatus).
Preload("User").
Find(&identityCards).Error; err != nil {
log.Printf("Error fetching identity cards by user registration status: %v", err)
return nil, fmt.Errorf("error fetching identity cards by user registration status: %w", err)
}
return identityCards, nil
} */
func (r *userProfileRepository) Update(ctx context.Context, userID string, user *model.User) error {
result := r.db.WithContext(ctx).
Model(&model.User{}). Model(&model.User{}).
Where("id = ?", userID). Where("id = ?", userID).
Updates(updates).Error Updates(user)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrUserNotFound
}
return nil
} }

View File

@ -1 +1,20 @@
package userprofile package userprofile
import (
"rijig/config"
"rijig/middleware"
"github.com/gofiber/fiber/v2"
)
func UserProfileRouter(api fiber.Router) {
userProfileRepo := NewUserProfileRepository(config.DB)
userProfileService := NewUserProfileService(userProfileRepo)
userProfileHandler := NewUserProfileHandler(userProfileService)
userRoute := api.Group("/userprofile")
userRoute.Use(middleware.AuthMiddleware())
userRoute.Get("/", userProfileHandler.GetUserProfile)
userRoute.Put("/update", userProfileHandler.UpdateUserProfile)
}

View File

@ -1 +1,160 @@
package userprofile package userprofile
import (
"context"
"errors"
"fmt"
"rijig/internal/authentication"
"rijig/internal/role"
"rijig/model"
"rijig/utils"
"time"
)
var (
ErrUserNotFound = errors.New("user tidak ditemukan")
)
type UserProfileService interface {
GetUserProfile(ctx context.Context, userID string) (*UserProfileResponseDTO, error)
UpdateRegistUserProfile(ctx context.Context, userID, deviceId string, req *RequestUserProfileDTO) (*authentication.AuthResponse, error)
}
type userProfileService struct {
repo UserProfileRepository
}
func NewUserProfileService(repo UserProfileRepository) UserProfileService {
return &userProfileService{
repo: repo,
}
}
func (s *userProfileService) GetUserProfile(ctx context.Context, userID string) (*UserProfileResponseDTO, error) {
user, err := s.repo.GetByID(ctx, userID)
if err != nil {
if errors.Is(err, ErrUserNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to get user profile: %w", err)
}
return s.mapToResponseDTO(user), nil
}
func (s *userProfileService) UpdateRegistUserProfile(ctx context.Context, userID, deviceId string, req *RequestUserProfileDTO) (*authentication.AuthResponse, error) {
_, err := s.repo.GetByID(ctx, userID)
if err != nil {
if errors.Is(err, ErrUserNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to get user: %w", err)
}
updateUser := &model.User{
Name: req.Name,
Gender: req.Gender,
Dateofbirth: req.Dateofbirth,
Placeofbirth: req.Placeofbirth,
Phone: req.Phone,
RegistrationProgress: utils.ProgressDataSubmitted,
}
if err := s.repo.Update(ctx, userID, updateUser); err != nil {
if errors.Is(err, ErrUserNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to update user profile: %w", err)
}
updatedUser, err := s.repo.GetByID(ctx, userID)
if err != nil {
if errors.Is(err, ErrUserNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, fmt.Errorf("failed to get updated user: %w", err)
}
tokenResponse, err := utils.GenerateTokenPair(
updatedUser.ID,
updatedUser.Role.RoleName,
// req.DeviceID,
deviceId,
updatedUser.RegistrationStatus,
int(updatedUser.RegistrationProgress),
)
if err != nil {
return nil, fmt.Errorf("gagal generate token: %v", err)
}
nextStep := utils.GetNextRegistrationStep(
updatedUser.Role.RoleName,
int(updatedUser.RegistrationProgress),
updateUser.RegistrationStatus,
)
return &authentication.AuthResponse{
Message: "Isi data diri berhasil",
AccessToken: tokenResponse.AccessToken,
RefreshToken: tokenResponse.RefreshToken,
TokenType: string(tokenResponse.TokenType),
ExpiresIn: tokenResponse.ExpiresIn,
RegistrationStatus: updateUser.RegistrationStatus,
NextStep: nextStep,
SessionID: tokenResponse.SessionID,
}, nil
// return s.mapToResponseDTO(updatedUser), nil
}
func (s *userProfileService) mapToResponseDTO(user *model.User) *UserProfileResponseDTO {
createdAt, err := utils.FormatDateToIndonesianFormat(user.CreatedAt)
if err != nil {
createdAt = user.CreatedAt.Format(time.RFC3339)
}
updatedAt, err := utils.FormatDateToIndonesianFormat(user.UpdatedAt)
if err != nil {
updatedAt = user.UpdatedAt.Format(time.RFC3339)
}
response := &UserProfileResponseDTO{
ID: user.ID,
Name: user.Name,
Gender: user.Gender,
Dateofbirth: user.Dateofbirth,
Placeofbirth: user.Placeofbirth,
Phone: user.Phone,
PhoneVerified: user.PhoneVerified,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
if user.Avatar != nil {
response.Avatar = *user.Avatar
}
if user.Role != nil {
roleCreatedAt, err := utils.FormatDateToIndonesianFormat(user.Role.CreatedAt)
if err != nil {
roleCreatedAt = user.Role.CreatedAt.Format(time.RFC3339)
}
roleUpdatedAt, err := utils.FormatDateToIndonesianFormat(user.Role.UpdatedAt)
if err != nil {
roleUpdatedAt = user.Role.UpdatedAt.Format(time.RFC3339)
}
response.Role = role.RoleResponseDTO{
ID: user.Role.ID,
RoleName: user.Role.RoleName,
CreatedAt: roleCreatedAt,
UpdatedAt: roleUpdatedAt,
}
}
return response
}

View File

@ -1 +1,272 @@
package wilayahindo package wilayahindo
import (
"strconv"
"strings"
"rijig/utils"
"github.com/gofiber/fiber/v2"
)
type WilayahIndonesiaHandler struct {
WilayahService WilayahIndonesiaService
}
func NewWilayahIndonesiaHandler(wilayahService WilayahIndonesiaService) *WilayahIndonesiaHandler {
return &WilayahIndonesiaHandler{
WilayahService: wilayahService,
}
}
func (h *WilayahIndonesiaHandler) ImportDataFromCSV(c *fiber.Ctx) error {
ctx := c.Context()
if err := h.WilayahService.ImportDataFromCSV(ctx); err != nil {
return utils.InternalServerError(c, "Failed to import data from CSV: "+err.Error())
}
return utils.Success(c, "Data imported successfully from CSV")
}
func (h *WilayahIndonesiaHandler) GetAllProvinces(c *fiber.Ctx) error {
ctx := c.Context()
page, limit, err := h.parsePaginationParams(c)
if err != nil {
return utils.BadRequest(c, err.Error())
}
provinces, total, err := h.WilayahService.GetAllProvinces(ctx, page, limit)
if err != nil {
return utils.InternalServerError(c, "Failed to fetch provinces: "+err.Error())
}
response := map[string]interface{}{
"provinces": provinces,
"total": total,
}
return utils.SuccessWithPagination(c, "Provinces retrieved successfully", response, page, limit)
}
func (h *WilayahIndonesiaHandler) GetProvinceByID(c *fiber.Ctx) error {
ctx := c.Context()
id := c.Params("id")
if id == "" {
return utils.BadRequest(c, "Province ID is required")
}
page, limit, err := h.parsePaginationParams(c)
if err != nil {
return utils.BadRequest(c, err.Error())
}
province, totalRegencies, err := h.WilayahService.GetProvinceByID(ctx, id, page, limit)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Province not found")
}
return utils.InternalServerError(c, "Failed to fetch province: "+err.Error())
}
response := map[string]interface{}{
"province": province,
"total_regencies": totalRegencies,
}
return utils.SuccessWithPagination(c, "Province retrieved successfully", response, page, limit)
}
func (h *WilayahIndonesiaHandler) GetAllRegencies(c *fiber.Ctx) error {
ctx := c.Context()
page, limit, err := h.parsePaginationParams(c)
if err != nil {
return utils.BadRequest(c, err.Error())
}
regencies, total, err := h.WilayahService.GetAllRegencies(ctx, page, limit)
if err != nil {
return utils.InternalServerError(c, "Failed to fetch regencies: "+err.Error())
}
response := map[string]interface{}{
"regencies": regencies,
"total": total,
}
return utils.SuccessWithPagination(c, "Regencies retrieved successfully", response, page, limit)
}
func (h *WilayahIndonesiaHandler) GetRegencyByID(c *fiber.Ctx) error {
ctx := c.Context()
id := c.Params("id")
if id == "" {
return utils.BadRequest(c, "Regency ID is required")
}
page, limit, err := h.parsePaginationParams(c)
if err != nil {
return utils.BadRequest(c, err.Error())
}
regency, totalDistricts, err := h.WilayahService.GetRegencyByID(ctx, id, page, limit)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Regency not found")
}
return utils.InternalServerError(c, "Failed to fetch regency: "+err.Error())
}
response := map[string]interface{}{
"regency": regency,
"total_districts": totalDistricts,
}
return utils.SuccessWithPagination(c, "Regency retrieved successfully", response, page, limit)
}
func (h *WilayahIndonesiaHandler) GetAllDistricts(c *fiber.Ctx) error {
ctx := c.Context()
page, limit, err := h.parsePaginationParams(c)
if err != nil {
return utils.BadRequest(c, err.Error())
}
districts, total, err := h.WilayahService.GetAllDistricts(ctx, page, limit)
if err != nil {
return utils.InternalServerError(c, "Failed to fetch districts: "+err.Error())
}
response := map[string]interface{}{
"districts": districts,
"total": total,
}
return utils.SuccessWithPagination(c, "Districts retrieved successfully", response, page, limit)
}
func (h *WilayahIndonesiaHandler) GetDistrictByID(c *fiber.Ctx) error {
ctx := c.Context()
id := c.Params("id")
if id == "" {
return utils.BadRequest(c, "District ID is required")
}
page, limit, err := h.parsePaginationParams(c)
if err != nil {
return utils.BadRequest(c, err.Error())
}
district, totalVillages, err := h.WilayahService.GetDistrictByID(ctx, id, page, limit)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "District not found")
}
return utils.InternalServerError(c, "Failed to fetch district: "+err.Error())
}
response := map[string]interface{}{
"district": district,
"total_villages": totalVillages,
}
return utils.SuccessWithPagination(c, "District retrieved successfully", response, page, limit)
}
func (h *WilayahIndonesiaHandler) GetAllVillages(c *fiber.Ctx) error {
ctx := c.Context()
page, limit, err := h.parsePaginationParams(c)
if err != nil {
return utils.BadRequest(c, err.Error())
}
villages, total, err := h.WilayahService.GetAllVillages(ctx, page, limit)
if err != nil {
return utils.InternalServerError(c, "Failed to fetch villages: "+err.Error())
}
response := map[string]interface{}{
"villages": villages,
"total": total,
}
return utils.SuccessWithPagination(c, "Villages retrieved successfully", response, page, limit)
}
func (h *WilayahIndonesiaHandler) GetVillageByID(c *fiber.Ctx) error {
ctx := c.Context()
id := c.Params("id")
if id == "" {
return utils.BadRequest(c, "Village ID is required")
}
village, err := h.WilayahService.GetVillageByID(ctx, id)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return utils.NotFound(c, "Village not found")
}
return utils.InternalServerError(c, "Failed to fetch village: "+err.Error())
}
return utils.SuccessWithData(c, "Village retrieved successfully", village)
}
func (h *WilayahIndonesiaHandler) parsePaginationParams(c *fiber.Ctx) (int, int, error) {
page := 1
limit := 10
if pageStr := c.Query("page"); pageStr != "" {
parsedPage, err := strconv.Atoi(pageStr)
if err != nil {
return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Invalid page parameter")
}
if parsedPage < 1 {
return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Page must be greater than 0")
}
page = parsedPage
}
if limitStr := c.Query("limit"); limitStr != "" {
parsedLimit, err := strconv.Atoi(limitStr)
if err != nil {
return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Invalid limit parameter")
}
if parsedLimit < 1 {
return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Limit must be greater than 0")
}
if parsedLimit > 100 {
return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Limit cannot exceed 100")
}
limit = parsedLimit
}
return page, limit, nil
}
func (h *WilayahIndonesiaHandler) SetupRoutes(app *fiber.App) {
api := app.Group("/api/v1/wilayah")
api.Post("/import", h.ImportDataFromCSV)
api.Get("/provinces", h.GetAllProvinces)
api.Get("/provinces/:id", h.GetProvinceByID)
api.Get("/regencies", h.GetAllRegencies)
api.Get("/regencies/:id", h.GetRegencyByID)
api.Get("/districts", h.GetAllDistricts)
api.Get("/districts/:id", h.GetDistrictByID)
api.Get("/villages", h.GetAllVillages)
api.Get("/villages/:id", h.GetVillageByID)
}

View File

@ -1 +1,32 @@
package wilayahindo package wilayahindo
import (
"rijig/config"
"rijig/middleware"
"github.com/gofiber/fiber/v2"
)
func WilayahRouter(api fiber.Router) {
wilayahRepo := NewWilayahIndonesiaRepository(config.DB)
wilayahService := NewWilayahIndonesiaService(wilayahRepo)
wilayahHandler := NewWilayahIndonesiaHandler(wilayahService)
api.Post("/import/data-wilayah-indonesia", middleware.RequireAdminRole(), wilayahHandler.ImportDataFromCSV)
wilayahAPI := api.Group("/wilayah-indonesia")
wilayahAPI.Get("/provinces", wilayahHandler.GetAllProvinces)
wilayahAPI.Get("/provinces/:provinceid", wilayahHandler.GetProvinceByID)
wilayahAPI.Get("/regencies", wilayahHandler.GetAllRegencies)
wilayahAPI.Get("/regencies/:regencyid", wilayahHandler.GetRegencyByID)
wilayahAPI.Get("/districts", wilayahHandler.GetAllDistricts)
wilayahAPI.Get("/districts/:districtid", wilayahHandler.GetDistrictByID)
wilayahAPI.Get("/villages", wilayahHandler.GetAllVillages)
wilayahAPI.Get("/villages/:villageid", wilayahHandler.GetVillageByID)
}

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"time" "time"
"rijig/dto"
"rijig/model" "rijig/model"
"rijig/utils" "rijig/utils"
) )
@ -13,17 +12,17 @@ import (
type WilayahIndonesiaService interface { type WilayahIndonesiaService interface {
ImportDataFromCSV(ctx context.Context) error ImportDataFromCSV(ctx context.Context) error
GetAllProvinces(ctx context.Context, page, limit int) ([]dto.ProvinceResponseDTO, int, error) GetAllProvinces(ctx context.Context, page, limit int) ([]ProvinceResponseDTO, int, error)
GetProvinceByID(ctx context.Context, id string, page, limit int) (*dto.ProvinceResponseDTO, int, error) GetProvinceByID(ctx context.Context, id string, page, limit int) (*ProvinceResponseDTO, int, error)
GetAllRegencies(ctx context.Context, page, limit int) ([]dto.RegencyResponseDTO, int, error) GetAllRegencies(ctx context.Context, page, limit int) ([]RegencyResponseDTO, int, error)
GetRegencyByID(ctx context.Context, id string, page, limit int) (*dto.RegencyResponseDTO, int, error) GetRegencyByID(ctx context.Context, id string, page, limit int) (*RegencyResponseDTO, int, error)
GetAllDistricts(ctx context.Context, page, limit int) ([]dto.DistrictResponseDTO, int, error) GetAllDistricts(ctx context.Context, page, limit int) ([]DistrictResponseDTO, int, error)
GetDistrictByID(ctx context.Context, id string, page, limit int) (*dto.DistrictResponseDTO, int, error) GetDistrictByID(ctx context.Context, id string, page, limit int) (*DistrictResponseDTO, int, error)
GetAllVillages(ctx context.Context, page, limit int) ([]dto.VillageResponseDTO, int, error) GetAllVillages(ctx context.Context, page, limit int) ([]VillageResponseDTO, int, error)
GetVillageByID(ctx context.Context, id string) (*dto.VillageResponseDTO, error) GetVillageByID(ctx context.Context, id string) (*VillageResponseDTO, error)
} }
type wilayahIndonesiaService struct { type wilayahIndonesiaService struct {
@ -122,11 +121,11 @@ func (s *wilayahIndonesiaService) ImportDataFromCSV(ctx context.Context) error {
return nil return nil
} }
func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, limit int) ([]dto.ProvinceResponseDTO, int, error) { func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, limit int) ([]ProvinceResponseDTO, int, error) {
cacheKey := fmt.Sprintf("provinces_page:%d_limit:%d", page, limit) cacheKey := fmt.Sprintf("provinces_page:%d_limit:%d", page, limit)
var cachedResponse struct { var cachedResponse struct {
Data []dto.ProvinceResponseDTO `json:"data"` Data []ProvinceResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
} }
@ -139,16 +138,16 @@ func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, lim
return nil, 0, fmt.Errorf("failed to fetch provinces: %w", err) return nil, 0, fmt.Errorf("failed to fetch provinces: %w", err)
} }
provinceDTOs := make([]dto.ProvinceResponseDTO, len(provinces)) provinceDTOs := make([]ProvinceResponseDTO, len(provinces))
for i, province := range provinces { for i, province := range provinces {
provinceDTOs[i] = dto.ProvinceResponseDTO{ provinceDTOs[i] = ProvinceResponseDTO{
ID: province.ID, ID: province.ID,
Name: province.Name, Name: province.Name,
} }
} }
cacheData := struct { cacheData := struct {
Data []dto.ProvinceResponseDTO `json:"data"` Data []ProvinceResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
}{ }{
Data: provinceDTOs, Data: provinceDTOs,
@ -162,11 +161,11 @@ func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, lim
return provinceDTOs, total, nil return provinceDTOs, total, nil
} }
func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string, page, limit int) (*dto.ProvinceResponseDTO, int, error) { func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string, page, limit int) (*ProvinceResponseDTO, int, error) {
cacheKey := fmt.Sprintf("province:%s_page:%d_limit:%d", id, page, limit) cacheKey := fmt.Sprintf("province:%s_page:%d_limit:%d", id, page, limit)
var cachedResponse struct { var cachedResponse struct {
Data dto.ProvinceResponseDTO `json:"data"` Data ProvinceResponseDTO `json:"data"`
TotalRegencies int `json:"total_regencies"` TotalRegencies int `json:"total_regencies"`
} }
@ -179,14 +178,14 @@ func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string
return nil, 0, err return nil, 0, err
} }
provinceDTO := dto.ProvinceResponseDTO{ provinceDTO := ProvinceResponseDTO{
ID: province.ID, ID: province.ID,
Name: province.Name, Name: province.Name,
} }
regencyDTOs := make([]dto.RegencyResponseDTO, len(province.Regencies)) regencyDTOs := make([]RegencyResponseDTO, len(province.Regencies))
for i, regency := range province.Regencies { for i, regency := range province.Regencies {
regencyDTOs[i] = dto.RegencyResponseDTO{ regencyDTOs[i] = RegencyResponseDTO{
ID: regency.ID, ID: regency.ID,
ProvinceID: regency.ProvinceID, ProvinceID: regency.ProvinceID,
Name: regency.Name, Name: regency.Name,
@ -195,7 +194,7 @@ func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string
provinceDTO.Regencies = regencyDTOs provinceDTO.Regencies = regencyDTOs
cacheData := struct { cacheData := struct {
Data dto.ProvinceResponseDTO `json:"data"` Data ProvinceResponseDTO `json:"data"`
TotalRegencies int `json:"total_regencies"` TotalRegencies int `json:"total_regencies"`
}{ }{
Data: provinceDTO, Data: provinceDTO,
@ -209,11 +208,11 @@ func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string
return &provinceDTO, totalRegencies, nil return &provinceDTO, totalRegencies, nil
} }
func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, limit int) ([]dto.RegencyResponseDTO, int, error) { func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, limit int) ([]RegencyResponseDTO, int, error) {
cacheKey := fmt.Sprintf("regencies_page:%d_limit:%d", page, limit) cacheKey := fmt.Sprintf("regencies_page:%d_limit:%d", page, limit)
var cachedResponse struct { var cachedResponse struct {
Data []dto.RegencyResponseDTO `json:"data"` Data []RegencyResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
} }
@ -226,9 +225,9 @@ func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, lim
return nil, 0, fmt.Errorf("failed to fetch regencies: %w", err) return nil, 0, fmt.Errorf("failed to fetch regencies: %w", err)
} }
regencyDTOs := make([]dto.RegencyResponseDTO, len(regencies)) regencyDTOs := make([]RegencyResponseDTO, len(regencies))
for i, regency := range regencies { for i, regency := range regencies {
regencyDTOs[i] = dto.RegencyResponseDTO{ regencyDTOs[i] = RegencyResponseDTO{
ID: regency.ID, ID: regency.ID,
ProvinceID: regency.ProvinceID, ProvinceID: regency.ProvinceID,
Name: regency.Name, Name: regency.Name,
@ -236,7 +235,7 @@ func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, lim
} }
cacheData := struct { cacheData := struct {
Data []dto.RegencyResponseDTO `json:"data"` Data []RegencyResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
}{ }{
Data: regencyDTOs, Data: regencyDTOs,
@ -250,11 +249,11 @@ func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, lim
return regencyDTOs, total, nil return regencyDTOs, total, nil
} }
func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string, page, limit int) (*dto.RegencyResponseDTO, int, error) { func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string, page, limit int) (*RegencyResponseDTO, int, error) {
cacheKey := fmt.Sprintf("regency:%s_page:%d_limit:%d", id, page, limit) cacheKey := fmt.Sprintf("regency:%s_page:%d_limit:%d", id, page, limit)
var cachedResponse struct { var cachedResponse struct {
Data dto.RegencyResponseDTO `json:"data"` Data RegencyResponseDTO `json:"data"`
TotalDistricts int `json:"total_districts"` TotalDistricts int `json:"total_districts"`
} }
@ -267,15 +266,15 @@ func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string,
return nil, 0, err return nil, 0, err
} }
regencyDTO := dto.RegencyResponseDTO{ regencyDTO := RegencyResponseDTO{
ID: regency.ID, ID: regency.ID,
ProvinceID: regency.ProvinceID, ProvinceID: regency.ProvinceID,
Name: regency.Name, Name: regency.Name,
} }
districtDTOs := make([]dto.DistrictResponseDTO, len(regency.Districts)) districtDTOs := make([]DistrictResponseDTO, len(regency.Districts))
for i, district := range regency.Districts { for i, district := range regency.Districts {
districtDTOs[i] = dto.DistrictResponseDTO{ districtDTOs[i] = DistrictResponseDTO{
ID: district.ID, ID: district.ID,
RegencyID: district.RegencyID, RegencyID: district.RegencyID,
Name: district.Name, Name: district.Name,
@ -284,7 +283,7 @@ func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string,
regencyDTO.Districts = districtDTOs regencyDTO.Districts = districtDTOs
cacheData := struct { cacheData := struct {
Data dto.RegencyResponseDTO `json:"data"` Data RegencyResponseDTO `json:"data"`
TotalDistricts int `json:"total_districts"` TotalDistricts int `json:"total_districts"`
}{ }{
Data: regencyDTO, Data: regencyDTO,
@ -298,11 +297,11 @@ func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string,
return &regencyDTO, totalDistricts, nil return &regencyDTO, totalDistricts, nil
} }
func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, limit int) ([]dto.DistrictResponseDTO, int, error) { func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, limit int) ([]DistrictResponseDTO, int, error) {
cacheKey := fmt.Sprintf("districts_page:%d_limit:%d", page, limit) cacheKey := fmt.Sprintf("districts_page:%d_limit:%d", page, limit)
var cachedResponse struct { var cachedResponse struct {
Data []dto.DistrictResponseDTO `json:"data"` Data []DistrictResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
} }
@ -315,9 +314,9 @@ func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, lim
return nil, 0, fmt.Errorf("failed to fetch districts: %w", err) return nil, 0, fmt.Errorf("failed to fetch districts: %w", err)
} }
districtDTOs := make([]dto.DistrictResponseDTO, len(districts)) districtDTOs := make([]DistrictResponseDTO, len(districts))
for i, district := range districts { for i, district := range districts {
districtDTOs[i] = dto.DistrictResponseDTO{ districtDTOs[i] = DistrictResponseDTO{
ID: district.ID, ID: district.ID,
RegencyID: district.RegencyID, RegencyID: district.RegencyID,
Name: district.Name, Name: district.Name,
@ -325,7 +324,7 @@ func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, lim
} }
cacheData := struct { cacheData := struct {
Data []dto.DistrictResponseDTO `json:"data"` Data []DistrictResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
}{ }{
Data: districtDTOs, Data: districtDTOs,
@ -339,11 +338,11 @@ func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, lim
return districtDTOs, total, nil return districtDTOs, total, nil
} }
func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string, page, limit int) (*dto.DistrictResponseDTO, int, error) { func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string, page, limit int) (*DistrictResponseDTO, int, error) {
cacheKey := fmt.Sprintf("district:%s_page:%d_limit:%d", id, page, limit) cacheKey := fmt.Sprintf("district:%s_page:%d_limit:%d", id, page, limit)
var cachedResponse struct { var cachedResponse struct {
Data dto.DistrictResponseDTO `json:"data"` Data DistrictResponseDTO `json:"data"`
TotalVillages int `json:"total_villages"` TotalVillages int `json:"total_villages"`
} }
@ -356,15 +355,15 @@ func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string
return nil, 0, err return nil, 0, err
} }
districtDTO := dto.DistrictResponseDTO{ districtDTO := DistrictResponseDTO{
ID: district.ID, ID: district.ID,
RegencyID: district.RegencyID, RegencyID: district.RegencyID,
Name: district.Name, Name: district.Name,
} }
villageDTOs := make([]dto.VillageResponseDTO, len(district.Villages)) villageDTOs := make([]VillageResponseDTO, len(district.Villages))
for i, village := range district.Villages { for i, village := range district.Villages {
villageDTOs[i] = dto.VillageResponseDTO{ villageDTOs[i] = VillageResponseDTO{
ID: village.ID, ID: village.ID,
DistrictID: village.DistrictID, DistrictID: village.DistrictID,
Name: village.Name, Name: village.Name,
@ -373,7 +372,7 @@ func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string
districtDTO.Villages = villageDTOs districtDTO.Villages = villageDTOs
cacheData := struct { cacheData := struct {
Data dto.DistrictResponseDTO `json:"data"` Data DistrictResponseDTO `json:"data"`
TotalVillages int `json:"total_villages"` TotalVillages int `json:"total_villages"`
}{ }{
Data: districtDTO, Data: districtDTO,
@ -387,11 +386,11 @@ func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string
return &districtDTO, totalVillages, nil return &districtDTO, totalVillages, nil
} }
func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limit int) ([]dto.VillageResponseDTO, int, error) { func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limit int) ([]VillageResponseDTO, int, error) {
cacheKey := fmt.Sprintf("villages_page:%d_limit:%d", page, limit) cacheKey := fmt.Sprintf("villages_page:%d_limit:%d", page, limit)
var cachedResponse struct { var cachedResponse struct {
Data []dto.VillageResponseDTO `json:"data"` Data []VillageResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
} }
@ -404,9 +403,9 @@ func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limi
return nil, 0, fmt.Errorf("failed to fetch villages: %w", err) return nil, 0, fmt.Errorf("failed to fetch villages: %w", err)
} }
villageDTOs := make([]dto.VillageResponseDTO, len(villages)) villageDTOs := make([]VillageResponseDTO, len(villages))
for i, village := range villages { for i, village := range villages {
villageDTOs[i] = dto.VillageResponseDTO{ villageDTOs[i] = VillageResponseDTO{
ID: village.ID, ID: village.ID,
DistrictID: village.DistrictID, DistrictID: village.DistrictID,
Name: village.Name, Name: village.Name,
@ -414,7 +413,7 @@ func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limi
} }
cacheData := struct { cacheData := struct {
Data []dto.VillageResponseDTO `json:"data"` Data []VillageResponseDTO `json:"data"`
Total int `json:"total"` Total int `json:"total"`
}{ }{
Data: villageDTOs, Data: villageDTOs,
@ -428,10 +427,10 @@ func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limi
return villageDTOs, total, nil return villageDTOs, total, nil
} }
func (s *wilayahIndonesiaService) GetVillageByID(ctx context.Context, id string) (*dto.VillageResponseDTO, error) { func (s *wilayahIndonesiaService) GetVillageByID(ctx context.Context, id string) (*VillageResponseDTO, error) {
cacheKey := fmt.Sprintf("village:%s", id) cacheKey := fmt.Sprintf("village:%s", id)
var cachedResponse dto.VillageResponseDTO var cachedResponse VillageResponseDTO
if err := utils.GetCache(cacheKey, &cachedResponse); err == nil { if err := utils.GetCache(cacheKey, &cachedResponse); err == nil {
return &cachedResponse, nil return &cachedResponse, nil
} }
@ -441,7 +440,7 @@ func (s *wilayahIndonesiaService) GetVillageByID(ctx context.Context, id string)
return nil, fmt.Errorf("village not found: %w", err) return nil, fmt.Errorf("village not found: %w", err)
} }
villageResponse := &dto.VillageResponseDTO{ villageResponse := &VillageResponseDTO{
ID: village.ID, ID: village.ID,
DistrictID: village.DistrictID, DistrictID: village.DistrictID,
Name: village.Name, Name: village.Name,

View File

@ -7,6 +7,7 @@ type IdentityCard struct {
UserID string `gorm:"not null" json:"userId"` UserID string `gorm:"not null" json:"userId"`
User User `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"user"` User User `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"user"`
Identificationumber string `gorm:"not null" json:"identificationumber"` Identificationumber string `gorm:"not null" json:"identificationumber"`
Fullname string `gorm:"not null" json:"fullname"`
Placeofbirth string `gorm:"not null" json:"placeofbirth"` Placeofbirth string `gorm:"not null" json:"placeofbirth"`
Dateofbirth string `gorm:"not null" json:"dateofbirth"` Dateofbirth string `gorm:"not null" json:"dateofbirth"`
Gender string `gorm:"not null" json:"gender"` Gender string `gorm:"not null" json:"gender"`

0
public/document/districts.csv Normal file → Executable file
View File

0
public/document/provinces.csv Normal file → Executable file
View File

0
public/document/regencies.csv Normal file → Executable file
View File

0
public/document/villages.csv Normal file → Executable file
View File

Can't render this file because it is too large.

View File

@ -9,7 +9,9 @@ import (
"rijig/internal/identitycart" "rijig/internal/identitycart"
"rijig/internal/role" "rijig/internal/role"
"rijig/internal/userpin" "rijig/internal/userpin"
"rijig/internal/userprofile"
"rijig/internal/whatsapp" "rijig/internal/whatsapp"
"rijig/internal/wilayahindo"
"rijig/middleware" "rijig/middleware"
// "rijig/presentation" // "rijig/presentation"
@ -19,8 +21,9 @@ import (
func SetupRoutes(app *fiber.App) { func SetupRoutes(app *fiber.App) {
apa := app.Group(os.Getenv("BASE_URL")) apa := app.Group(os.Getenv("BASE_URL"))
whatsapp.WhatsAppRouter(apa)
apa.Static("/uploads", "./public"+os.Getenv("BASE_URL")+"/uploads") apa.Static("/uploads", "./public"+os.Getenv("BASE_URL")+"/uploads")
a := app.Group(os.Getenv("BASE_URL"))
whatsapp.WhatsAppRouter(a)
api := app.Group(os.Getenv("BASE_URL")) api := app.Group(os.Getenv("BASE_URL"))
api.Use(middleware.APIKeyMiddleware) api.Use(middleware.APIKeyMiddleware)
@ -32,6 +35,8 @@ func SetupRoutes(app *fiber.App) {
role.UserRoleRouter(api) role.UserRoleRouter(api)
article.ArticleRouter(api) article.ArticleRouter(api)
userprofile.UserProfileRouter(api)
wilayahindo.WilayahRouter(api)
// || auth router || // // || auth router || //
// presentation.AuthRouter(api) // presentation.AuthRouter(api)
@ -58,5 +63,4 @@ func SetupRoutes(app *fiber.App) {
// // presentation.AboutRouter(api) // // presentation.AboutRouter(api)
// presentation.TrashRouter(api) // presentation.TrashRouter(api)
// presentation.CoverageAreaRouter(api) // presentation.CoverageAreaRouter(api)
whatsapp.WhatsAppRouter(api)
} }