feat: add feature initialcoin

This commit is contained in:
pahmiudahgede 2025-02-19 01:21:55 +07:00
parent 39e61d629e
commit 8f475d1a0b
9 changed files with 542 additions and 0 deletions

View File

@ -45,6 +45,7 @@ func ConnectDatabase() {
&model.Address{},
&model.Article{},
&model.Banner{},
&model.InitialCoint{},
// ==main feature==
)
if err != nil {

34
dto/initialcoint_dto.go Normal file
View File

@ -0,0 +1,34 @@
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

@ -0,0 +1,98 @@
package handler
import (
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/services"
"github.com/pahmiudahgede/senggoldong/utils"
)
type InitialCointHandler struct {
InitialCointService services.InitialCointService
}
func NewInitialCointHandler(initialCointService services.InitialCointService) *InitialCointHandler {
return &InitialCointHandler{InitialCointService: initialCointService}
}
func (h *InitialCointHandler) CreateInitialCoint(c *fiber.Ctx) error {
var request dto.RequestInitialCointDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateCointInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
initialCointResponse, err := h.InitialCointService.CreateInitialCoint(request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
}
return utils.CreateResponse(c, initialCointResponse, "Initial coint created successfully")
}
func (h *InitialCointHandler) GetAllInitialCoints(c *fiber.Ctx) error {
initialCoints, err := h.InitialCointService.GetAllInitialCoints()
if err != nil {
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch initial coints")
}
return utils.NonPaginatedResponse(c, initialCoints, len(initialCoints), "Initial coints fetched successfully")
}
func (h *InitialCointHandler) GetInitialCointByID(c *fiber.Ctx) error {
id := c.Params("coin_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
}
initialCoint, err := h.InitialCointService.GetInitialCointByID(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, "Invalid coin ID")
}
return utils.SuccessResponse(c, initialCoint, "Initial coint fetched successfully")
}
func (h *InitialCointHandler) UpdateInitialCoint(c *fiber.Ctx) error {
id := c.Params("coin_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
}
var request dto.RequestInitialCointDTO
if err := c.BodyParser(&request); err != nil {
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
}
errors, valid := request.ValidateCointInput()
if !valid {
return utils.ValidationErrorResponse(c, errors)
}
initialCointResponse, err := h.InitialCointService.UpdateInitialCoint(id, request)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.SuccessResponse(c, initialCointResponse, "Initial coint updated successfully")
}
func (h *InitialCointHandler) DeleteInitialCoint(c *fiber.Ctx) error {
id := c.Params("coin_id")
if id == "" {
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
}
err := h.InitialCointService.DeleteInitialCoint(id)
if err != nil {
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
}
return utils.GenericResponse(c, fiber.StatusOK, "Initial coint deleted successfully")
}

View File

@ -0,0 +1,67 @@
package repositories
import (
"fmt"
"github.com/pahmiudahgede/senggoldong/model"
"gorm.io/gorm"
)
type InitialCointRepository interface {
CreateInitialCoint(coint *model.InitialCoint) error
FindInitialCointByID(id string) (*model.InitialCoint, error)
FindAllInitialCoints() ([]model.InitialCoint, error)
UpdateInitialCoint(id string, coint *model.InitialCoint) error
DeleteInitialCoint(id string) error
}
type initialCointRepository struct {
DB *gorm.DB
}
func NewInitialCointRepository(db *gorm.DB) InitialCointRepository {
return &initialCointRepository{DB: db}
}
func (r *initialCointRepository) CreateInitialCoint(coint *model.InitialCoint) error {
if err := r.DB.Create(coint).Error; err != nil {
return fmt.Errorf("failed to create initial coint: %v", err)
}
return nil
}
func (r *initialCointRepository) FindInitialCointByID(id string) (*model.InitialCoint, error) {
var coint model.InitialCoint
err := r.DB.Where("id = ?", id).First(&coint).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("initial coint with ID %s not found", id)
}
return nil, fmt.Errorf("failed to fetch initial coint by ID: %v", err)
}
return &coint, nil
}
func (r *initialCointRepository) FindAllInitialCoints() ([]model.InitialCoint, error) {
var coints []model.InitialCoint
err := r.DB.Find(&coints).Error
if err != nil {
return nil, fmt.Errorf("failed to fetch initial coints: %v", err)
}
return coints, nil
}
func (r *initialCointRepository) UpdateInitialCoint(id string, coint *model.InitialCoint) error {
err := r.DB.Model(&model.InitialCoint{}).Where("id = ?", id).Updates(coint).Error
if err != nil {
return fmt.Errorf("failed to update initial coint: %v", err)
}
return nil
}
func (r *initialCointRepository) DeleteInitialCoint(id string) error {
result := r.DB.Delete(&model.InitialCoint{}, "id = ?", id)
if result.Error != nil {
return fmt.Errorf("failed to delete initial coint: %v", result.Error)
}
return nil
}

View File

@ -0,0 +1,299 @@
package services
import (
"fmt"
"time"
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/model"
"github.com/pahmiudahgede/senggoldong/utils"
)
type InitialCointService interface {
CreateInitialCoint(request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error)
GetAllInitialCoints() ([]dto.ReponseInitialCointDTO, error)
GetInitialCointByID(id string) (*dto.ReponseInitialCointDTO, error)
UpdateInitialCoint(id string, request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error)
DeleteInitialCoint(id string) error
}
type initialCointService struct {
InitialCointRepo repositories.InitialCointRepository
}
func NewInitialCointService(repo repositories.InitialCointRepository) InitialCointService {
return &initialCointService{InitialCointRepo: repo}
}
func (s *initialCointService) CreateInitialCoint(request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error) {
errors, valid := request.ValidateCointInput()
if !valid {
return nil, fmt.Errorf("validation error: %v", errors)
}
coint := model.InitialCoint{
CoinName: request.CoinName,
ValuePerUnit: request.ValuePerUnit,
}
if err := s.InitialCointRepo.CreateInitialCoint(&coint); err != nil {
return nil, fmt.Errorf("failed to create initial coint: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(coint.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(coint.UpdatedAt)
responseDTO := &dto.ReponseInitialCointDTO{
ID: coint.ID,
CoinName: coint.CoinName,
ValuePerUnit: coint.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("initialcoint:%s", coint.ID)
cacheData := map[string]interface{}{
"data": responseDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching new initial coint: %v\n", err)
}
err := s.updateAllCointCache()
if err != nil {
return nil, fmt.Errorf("error updating all initial coint cache: %v", err)
}
return responseDTO, nil
}
func (s *initialCointService) GetAllInitialCoints() ([]dto.ReponseInitialCointDTO, error) {
var cointsDTO []dto.ReponseInitialCointDTO
cacheKey := "initialcoints:all"
cachedData, err := utils.GetJSONData(cacheKey)
if err != nil {
fmt.Printf("Error fetching cache for initialcoints: %v\n", err)
}
if cachedData != nil {
if data, ok := cachedData["data"].([]interface{}); ok {
for _, item := range data {
if cointData, ok := item.(map[string]interface{}); ok {
if coinID, ok := cointData["coin_id"].(string); ok {
if coinName, ok := cointData["coin_name"].(string); ok {
if valuePerUnit, ok := cointData["value_perunit"].(float64); ok {
if createdAt, ok := cointData["createdAt"].(string); ok {
if updatedAt, ok := cointData["updatedAt"].(string); ok {
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: coinID,
CoinName: coinName,
ValuePerUnit: valuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
}
}
}
}
}
}
return cointsDTO, nil
}
}
records, err := s.InitialCointRepo.FindAllInitialCoints()
if err != nil {
return nil, fmt.Errorf("failed to fetch initial coints from database: %v", err)
}
if len(records) == 0 {
return cointsDTO, nil
}
for _, record := range records {
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: record.ID,
CoinName: record.CoinName,
ValuePerUnit: record.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheData := map[string]interface{}{
"data": cointsDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching all initial coints: %v\n", err)
}
return cointsDTO, nil
}
func (s *initialCointService) GetInitialCointByID(id string) (*dto.ReponseInitialCointDTO, error) {
cacheKey := fmt.Sprintf("initialcoint:%s", id)
cachedData, err := utils.GetJSONData(cacheKey)
if err == nil && cachedData != nil {
if data, ok := cachedData["data"].(map[string]interface{}); ok {
return &dto.ReponseInitialCointDTO{
ID: data["coin_id"].(string),
CoinName: data["coin_name"].(string),
ValuePerUnit: data["value_perunit"].(float64),
CreatedAt: data["createdAt"].(string),
UpdatedAt: data["updatedAt"].(string),
}, nil
} else {
return nil, fmt.Errorf("error: cache data is not in the expected format for coin ID %s", id)
}
}
coint, err := s.InitialCointRepo.FindInitialCointByID(id)
if err != nil {
return nil, fmt.Errorf("failed to fetch initial coint by ID %s: %v", id, err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(coint.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(coint.UpdatedAt)
cointDTO := &dto.ReponseInitialCointDTO{
ID: coint.ID,
CoinName: coint.CoinName,
ValuePerUnit: coint.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheData := map[string]interface{}{
"data": cointDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching initial coint by ID: %v\n", err)
}
return cointDTO, nil
}
func (s *initialCointService) UpdateInitialCoint(id string, request dto.RequestInitialCointDTO) (*dto.ReponseInitialCointDTO, error) {
coint, err := s.InitialCointRepo.FindInitialCointByID(id)
if err != nil {
return nil, fmt.Errorf("initial coint with ID %s not found", id)
}
coint.CoinName = request.CoinName
coint.ValuePerUnit = request.ValuePerUnit
if err := s.InitialCointRepo.UpdateInitialCoint(id, coint); err != nil {
return nil, fmt.Errorf("failed to update initial coint: %v", err)
}
createdAt, _ := utils.FormatDateToIndonesianFormat(coint.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(coint.UpdatedAt)
cointDTO := &dto.ReponseInitialCointDTO{
ID: coint.ID,
CoinName: coint.CoinName,
ValuePerUnit: coint.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}
cacheKey := fmt.Sprintf("initialcoint:%s", id)
cacheData := map[string]interface{}{
"data": cointDTO,
}
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
fmt.Printf("Error caching updated initial coint: %v\n", err)
}
allCoints, err := s.InitialCointRepo.FindAllInitialCoints()
if err != nil {
return nil, fmt.Errorf("failed to fetch all initial coints from database: %v", err)
}
var cointsDTO []dto.ReponseInitialCointDTO
for _, record := range allCoints {
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: record.ID,
CoinName: record.CoinName,
ValuePerUnit: record.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheAllKey := "initialcoints:all"
cacheAllData := map[string]interface{}{
"data": cointsDTO,
}
if err := utils.SetJSONData(cacheAllKey, cacheAllData, time.Hour*24); err != nil {
fmt.Printf("Error caching all initial coints: %v\n", err)
}
return cointDTO, nil
}
func (s *initialCointService) DeleteInitialCoint(id string) error {
coint, err := s.InitialCointRepo.FindInitialCointByID(id)
if err != nil {
return fmt.Errorf("initial coint with ID %s not found", id)
}
if err := s.InitialCointRepo.DeleteInitialCoint(id); err != nil {
return fmt.Errorf("failed to delete initial coint: %v", err)
}
cacheKey := fmt.Sprintf("initialcoint:%s", coint.ID)
if err := utils.DeleteData(cacheKey); err != nil {
fmt.Printf("Error deleting cache for initial coint: %v\n", err)
}
return s.updateAllCointCache()
}
func (s *initialCointService) updateAllCointCache() error {
records, err := s.InitialCointRepo.FindAllInitialCoints()
if err != nil {
return fmt.Errorf("failed to fetch all initial coints from database: %v", err)
}
var cointsDTO []dto.ReponseInitialCointDTO
for _, record := range records {
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
cointsDTO = append(cointsDTO, dto.ReponseInitialCointDTO{
ID: record.ID,
CoinName: record.CoinName,
ValuePerUnit: record.ValuePerUnit,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
cacheAllKey := "initialcoints:all"
cacheAllData := map[string]interface{}{
"data": cointsDTO,
}
if err := utils.SetJSONData(cacheAllKey, cacheAllData, time.Hour*24); err != nil {
fmt.Printf("Error caching all initial coints: %v\n", err)
return err
}
return nil
}

View File

@ -0,0 +1,11 @@
package model
import "time"
type InitialCoint struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
CoinName string `gorm:"not null" json:"coin_name"`
ValuePerUnit float64 `gorm:"not null" json:"value_perunit"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
}

View File

@ -0,0 +1,27 @@
package presentation
import (
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/internal/handler"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/internal/services"
"github.com/pahmiudahgede/senggoldong/middleware"
"github.com/pahmiudahgede/senggoldong/utils"
)
func InitialCointRoute(api fiber.Router) {
initialCointRepo := repositories.NewInitialCointRepository(config.DB)
initialCointService := services.NewInitialCointService(initialCointRepo)
initialCointHandler := handler.NewInitialCointHandler(initialCointService)
initialCoint := api.Group("/initialcoint")
initialCoint.Use(middleware.AuthMiddleware, middleware.RoleMiddleware(utils.RoleAdministrator))
initialCoint.Post("/create", initialCointHandler.CreateInitialCoint)
initialCoint.Get("/getall", initialCointHandler.GetAllInitialCoints)
initialCoint.Get("/get/:coin_id", initialCointHandler.GetInitialCointByID)
initialCoint.Put("/update/:coin_id", initialCointHandler.UpdateInitialCoint)
initialCoint.Delete("/delete/:coin_id", initialCointHandler.DeleteInitialCoint)
}

View File

@ -18,4 +18,5 @@ func SetupRoutes(app *fiber.App) {
presentation.AddressRouter(api)
presentation.ArticleRouter(api)
presentation.BannerRouter(api)
presentation.InitialCointRoute(api)
}

View File

@ -81,6 +81,10 @@ func GetJSONData(key string) (map[string]interface{}, error) {
return nil, err
}
if data == nil {
return nil, fmt.Errorf("error: no data found for key %s", key)
}
return data, nil
}