Add License

This commit is contained in:
aditya.siregar 2024-07-28 13:00:01 +07:00
parent dfed117d90
commit 8966037024
11 changed files with 506 additions and 1 deletions

View File

@ -0,0 +1,78 @@
package entity
import (
"github.com/google/uuid"
"time"
)
type License struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:uuid_generate_v4()"`
PartnerID int64 `gorm:"type:bigint;not null"`
Name string `gorm:"type:varchar(255);not null"`
StartDate time.Time `gorm:"type:date;not null"`
EndDate time.Time `gorm:"type:date;not null"`
RenewalDate *time.Time `gorm:"type:date"`
SerialNumber string `gorm:"type:varchar(255);unique;not null"`
CreatedBy int64 `gorm:"type:bigint;not null"`
UpdatedBy int64 `gorm:"type:bigint;not null"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
func (License) TableName() string {
return "licenses"
}
type LicenseDB struct {
License
}
func (l *License) ToLicenseDB() *LicenseDB {
return &LicenseDB{
License: *l,
}
}
func (LicenseDB) TableName() string {
return "licenses"
}
func (e *LicenseDB) ToLicense() *License {
return &License{
ID: e.ID,
PartnerID: e.PartnerID,
Name: e.Name,
StartDate: e.StartDate,
EndDate: e.EndDate,
RenewalDate: e.RenewalDate,
SerialNumber: e.SerialNumber,
CreatedBy: e.CreatedBy,
UpdatedBy: e.UpdatedBy,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
}
}
func (o *LicenseDB) ToUpdatedLicense(updatedBy int64, req License) {
o.UpdatedBy = updatedBy
if req.Name != "" {
o.Name = req.Name
}
if !req.StartDate.IsZero() {
o.StartDate = req.StartDate
}
if !req.EndDate.IsZero() {
o.EndDate = req.EndDate
}
if req.RenewalDate != nil {
o.RenewalDate = req.RenewalDate
}
if req.SerialNumber != "" {
o.SerialNumber = req.SerialNumber
}
}

View File

@ -0,0 +1,156 @@
package license
import (
"furtuna-be/internal/common/errors"
"furtuna-be/internal/handlers/request"
"furtuna-be/internal/handlers/response"
"furtuna-be/internal/services"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"net/http"
)
type Handler struct {
service services.License
}
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/license")
route.POST("/", jwt, h.Create)
route.GET("/", jwt, h.GetAll)
route.PUT("/:id", jwt, h.Update)
route.GET("/:id", jwt, h.GetByID)
}
func NewHandler(service services.License) *Handler {
return &Handler{
service: service,
}
}
// Create handles the creation of a new license.
func (h *Handler) Create(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.License
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
licenseEntity, err := req.ToEntity()
if err != nil {
response.ErrorWrapper(c, err)
return
}
res, err := h.service.Create(ctx, licenseEntity)
if err != nil {
response.ErrorWrapper(c, err)
return
}
var licenseResponse response.License
licenseResponse.FromEntity(res)
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: licenseResponse,
})
}
// Update handles the update of an existing license.
func (h *Handler) Update(c *gin.Context) {
ctx := request.GetMyContext(c)
licenseID := c.Param("id")
var req request.License
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
licenseEntity, err := req.ToEntity()
if err != nil {
response.ErrorWrapper(c, err)
return
}
updatedLicense, err := h.service.Update(ctx, licenseID, licenseEntity)
if err != nil {
response.ErrorWrapper(c, err)
return
}
var licenseResponse response.License
licenseResponse.FromEntity(updatedLicense)
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: licenseResponse,
})
}
// GetAll retrieves a list of licenses.
func (h *Handler) GetAll(c *gin.Context) {
var req request.LicenseParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
licenses, total, err := h.service.GetAll(c.Request.Context(), req.Limit, req.Offset)
if err != nil {
response.ErrorWrapper(c, err)
return
}
licenseResponses := response.FromEntityList(licenses)
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.LicenseList{Licenses: licenseResponses, Total: total, Limit: req.Limit, Offset: req.Offset},
})
}
// GetByID retrieves details of a specific license by ID.
func (h *Handler) GetByID(c *gin.Context) {
licenseID := c.Param("id")
res, err := h.service.GetByID(c.Request.Context(), licenseID)
if err != nil {
c.JSON(http.StatusInternalServerError, response.BaseResponse{
Success: false,
Status: http.StatusInternalServerError,
Message: err.Error(),
Data: nil,
})
return
}
var licenseResponse response.License
licenseResponse.FromEntity(res)
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: licenseResponse,
})
}

View File

@ -0,0 +1,51 @@
package request
import (
"furtuna-be/internal/entity"
"time"
"github.com/google/uuid"
)
type License struct {
Name string `json:"name" validate:"required"`
StartDate string `json:"start_date" validate:"required"`
EndDate string `json:"end_date" validate:"required"`
RenewalDate string `json:"renewal_date"`
SerialNumber string `json:"serial_number" validate:"required"`
PartnerID int64 `json:"partner_id" validate:"required"`
}
type LicenseParam struct {
Limit int `form:"limit,default=10"`
Offset int `form:"offset,default=0"`
}
func (r *License) ToEntity() (*entity.License, error) {
startDate, err := time.Parse("2006-01-02", r.StartDate)
if err != nil {
return nil, err
}
endDate, err := time.Parse("2006-01-02", r.EndDate)
if err != nil {
return nil, err
}
var renewalDate *time.Time
if r.RenewalDate != "" {
parsedRenewalDate, err := time.Parse("2006-01-02", r.RenewalDate)
if err != nil {
return nil, err
}
renewalDate = &parsedRenewalDate
}
return &entity.License{
ID: uuid.New(),
Name: r.Name,
StartDate: startDate,
EndDate: endDate,
RenewalDate: renewalDate,
SerialNumber: r.SerialNumber,
PartnerID: r.PartnerID,
}, nil
}

View File

@ -0,0 +1,46 @@
package response
import (
"furtuna-be/internal/entity"
)
type License struct {
ID string `json:"id"`
Name string `json:"name"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
RenewalDate string `json:"renewal_date,omitempty"`
SerialNumber string `json:"serial_number"`
PartnerID int64 `json:"partner_id"`
CreatedBy int64 `json:"created_by"`
UpdatedBy int64 `json:"updated_by"`
}
type LicenseList struct {
Licenses []License `json:"licenses"`
Total int64 `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
func (r *License) FromEntity(e *entity.License) {
r.ID = e.ID.String()
r.Name = e.Name
r.StartDate = e.StartDate.Format("2006-01-02")
r.EndDate = e.EndDate.Format("2006-01-02")
if e.RenewalDate != nil {
r.RenewalDate = e.RenewalDate.Format("2006-01-02")
}
r.SerialNumber = e.SerialNumber
r.PartnerID = e.PartnerID
r.CreatedBy = e.CreatedBy
r.UpdatedBy = e.UpdatedBy
}
func FromEntityList(entities []*entity.License) []License {
licenses := make([]License, len(entities))
for i, e := range entities {
licenses[i].FromEntity(e)
}
return licenses
}

View File

@ -0,0 +1,66 @@
package license
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"github.com/google/uuid"
"go.uber.org/zap"
"gorm.io/gorm"
)
type LicenseRepository struct {
db *gorm.DB
}
func NewLicenseRepository(db *gorm.DB) *LicenseRepository {
return &LicenseRepository{
db: db,
}
}
func (r *LicenseRepository) Create(ctx context.Context, license *entity.LicenseDB) (*entity.LicenseDB, error) {
if err := r.db.WithContext(ctx).Create(license).Error; err != nil {
logger.ContextLogger(ctx).Error("error when creating license", zap.Error(err))
return nil, err
}
return license, nil
}
func (r *LicenseRepository) Update(ctx context.Context, license *entity.LicenseDB) (*entity.LicenseDB, error) {
if err := r.db.WithContext(ctx).Save(license).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating license", zap.Error(err))
return nil, err
}
return license, nil
}
func (r *LicenseRepository) FindByID(ctx context.Context, id string) (*entity.LicenseDB, error) {
licenseID, err := uuid.Parse(id)
if err != nil {
return nil, err
}
license := new(entity.LicenseDB)
if err := r.db.WithContext(ctx).First(license, licenseID).Error; err != nil {
logger.ContextLogger(ctx).Error("error when finding license by ID", zap.Error(err))
return nil, err
}
return license, nil
}
func (r *LicenseRepository) GetAll(ctx context.Context, limit, offset int) ([]*entity.License, int64, error) {
var licenses []*entity.License
var total int64
if err := r.db.WithContext(ctx).
Limit(limit).
Offset(offset).
Find(&licenses).
Count(&total).
Error; err != nil {
return nil, 0, err
}
return licenses, total, nil
}

View File

@ -5,6 +5,7 @@ import (
"database/sql"
"furtuna-be/internal/repository/branches"
"furtuna-be/internal/repository/brevo"
"furtuna-be/internal/repository/license"
mdtrns "furtuna-be/internal/repository/midtrans"
"furtuna-be/internal/repository/orders"
"furtuna-be/internal/repository/oss"
@ -43,6 +44,7 @@ type RepoManagerImpl struct {
Midtrans Midtrans
Payment Payment
EmailService EmailService
License License
}
func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
@ -63,6 +65,7 @@ func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
Midtrans: mdtrns.New(&cfg.Midtrans),
Payment: payment.NewPaymentRepository(db),
EmailService: brevo.New(&cfg.Brevo),
License: license.NewLicenseRepository(db),
}
}
@ -183,3 +186,10 @@ type Payment interface {
type EmailService interface {
SendEmailTransactional(ctx context.Context, param entity.SendEmailNotificationParam) error
}
type License interface {
Create(ctx context.Context, license *entity.LicenseDB) (*entity.LicenseDB, error)
Update(ctx context.Context, license *entity.LicenseDB) (*entity.LicenseDB, error)
FindByID(ctx context.Context, id string) (*entity.LicenseDB, error)
GetAll(ctx context.Context, limit, offset int) ([]*entity.License, int64, error)
}

View File

@ -2,6 +2,7 @@ package routes
import (
"furtuna-be/internal/handlers/http/branch"
"furtuna-be/internal/handlers/http/license"
mdtrns "furtuna-be/internal/handlers/http/midtrans"
"furtuna-be/internal/handlers/http/order"
"furtuna-be/internal/handlers/http/oss"
@ -56,6 +57,7 @@ func RegisterPrivateRoutes(app *app.Server, serviceManager *services.ServiceMana
partner.NewHandler(serviceManager.PartnerSvc),
site.NewHandler(serviceManager.SiteSvc),
mdtrns.NewHandler(serviceManager.OrderSvc),
license.NewHandler(serviceManager.LicenseSvc),
}
for _, handler := range serverRoutes {

View File

@ -0,0 +1,71 @@
package service
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/common/mycontext"
"furtuna-be/internal/entity"
"furtuna-be/internal/repository"
"go.uber.org/zap"
)
type LicenseService struct {
repo repository.License
}
func NewLicenseService(repo repository.License) *LicenseService {
return &LicenseService{
repo: repo,
}
}
func (s *LicenseService) Create(ctx mycontext.Context, licenseReq *entity.License) (*entity.License, error) {
licenseDB := licenseReq.ToLicenseDB()
licenseDB.CreatedBy = ctx.RequestedBy()
licenseDB.UpdatedBy = ctx.RequestedBy()
licenseDB, err := s.repo.Create(ctx, licenseDB)
if err != nil {
logger.ContextLogger(ctx).Error("error when create license", zap.Error(err))
return nil, err
}
return licenseDB.ToLicense(), nil
}
func (s *LicenseService) Update(ctx mycontext.Context, id string, licenseReq *entity.License) (*entity.License, error) {
existingLicense, err := s.repo.FindByID(ctx, id)
if err != nil {
return nil, err
}
existingLicense.ToUpdatedLicense(ctx.RequestedBy(), *licenseReq)
updatedLicenseDB, err := s.repo.Update(ctx, existingLicense)
if err != nil {
logger.ContextLogger(ctx).Error("error when update license", zap.Error(err))
return nil, err
}
return updatedLicenseDB.ToLicense(), nil
}
func (s *LicenseService) GetByID(ctx context.Context, id string) (*entity.License, error) {
licenseDB, err := s.repo.FindByID(ctx, id)
if err != nil {
logger.ContextLogger(ctx).Error("error when get license by id", zap.Error(err))
return nil, err
}
return licenseDB.ToLicense(), nil
}
func (s *LicenseService) GetAll(ctx context.Context, limit, offset int) ([]*entity.License, int64, error) {
licenses, total, err := s.repo.GetAll(ctx, limit, offset)
if err != nil {
logger.ContextLogger(ctx).Error("error when getting all licenses", zap.Error(err))
return nil, 0, err
}
return licenses, total, nil
}

View File

@ -4,6 +4,7 @@ import (
"context"
"furtuna-be/internal/common/mycontext"
"furtuna-be/internal/services/branch"
service "furtuna-be/internal/services/license"
"furtuna-be/internal/services/order"
"furtuna-be/internal/services/oss"
"furtuna-be/internal/services/partner"
@ -32,6 +33,7 @@ type ServiceManagerImpl struct {
OSSSvc OSSService
PartnerSvc Partner
SiteSvc Site
LicenseSvc License
}
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
@ -46,7 +48,8 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
OSSSvc: oss.NewOSSService(repo.OSS),
PartnerSvc: partner.NewPartnerService(
repo.Partner, users.NewUserService(repo.User, repo.Branch), repo.Trx, repo.Wallet),
SiteSvc: site.NewSiteService(repo.Site),
SiteSvc: site.NewSiteService(repo.Site),
LicenseSvc: service.NewLicenseService(repo.License),
}
}
@ -123,3 +126,10 @@ type Site interface {
GetAll(ctx context.Context, search entity.SiteSearch) ([]*entity.Site, int, error)
Delete(ctx mycontext.Context, id int64) error
}
type License interface {
Create(ctx mycontext.Context, licenseReq *entity.License) (*entity.License, error)
Update(ctx mycontext.Context, id string, licenseReq *entity.License) (*entity.License, error)
GetByID(ctx context.Context, id string) (*entity.License, error)
GetAll(ctx context.Context, limit, offset int) ([]*entity.License, int64, error)
}

View File

@ -0,0 +1 @@
DROP TABLE licenses cascade;

View File

@ -0,0 +1,14 @@
CREATE TABLE licenses
(
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
partner_id NUMERIC NOT NULL,
name VARCHAR(255) NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
renewal_date DATE,
serial_number VARCHAR(255) UNIQUE NOT NULL,
created_by numeric not null,
updated_by numeric not null,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);