Add User Partner and Product

This commit is contained in:
aditya.siregar 2024-06-03 14:40:50 +07:00
parent 67f1dbc850
commit 8a23e72230
46 changed files with 1366 additions and 213 deletions

View File

@ -30,6 +30,3 @@ deploy_to_staging:
- kubectl apply -f k8s/staging/ingress.yaml
only:
- main
# tes bintang 4

View File

@ -1,9 +1,9 @@
PROJECT_NAME = "furtuna-backend"
DB_USERNAME := furtuna_admin
DB_USERNAME := fortuna_admin
DB_PASSWORD := Z4G827t9428QFQ%5ESZXW%2343dB%25%214Bmh80
DB_HOST := 103.96.146.124
DB_PORT := 1960
DB_NAME := furtuna-staging
DB_NAME := fortuna-staging
DB_URL = postgres://$(DB_USERNAME):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?sslmode=disable

1
furtuna-frontend Submodule

@ -0,0 +1 @@
Subproject commit 19af799abdd523522fad579044d55f5d764f87a3

View File

@ -72,6 +72,9 @@ func (s *ServiceException) MapErrorsToHTTPCode() int {
case errInvalidLogin:
return http.StatusBadRequest
case errUserIsNotFound:
return http.StatusBadRequest
default:
return http.StatusInternalServerError
}
@ -89,6 +92,9 @@ func (s *ServiceException) MapErrorsToCode() Code {
case errBadRequest:
return BadRequest
case errUserIsNotFound:
return BadRequest
default:
return ServerError
}

View File

@ -13,6 +13,7 @@ type Context interface {
RequestedBy() int64
IsSuperAdmin() bool
GetPartnerID() *int64
}
type MyContextImpl struct {
@ -20,7 +21,7 @@ type MyContextImpl struct {
requestedBy int64
requestID string
branchID int64
partnerID int64
roleID int
}
@ -32,11 +33,18 @@ func (m *MyContextImpl) IsSuperAdmin() bool {
return m.roleID == int(role.SuperAdmin)
}
func (m *MyContextImpl) GetPartnerID() *int64 {
if m.partnerID != 0 {
return &m.partnerID
}
return nil
}
func NewMyContext(parent context.Context, claims *entity.JWTAuthClaims) (*MyContextImpl, error) {
return &MyContextImpl{
Context: parent,
requestedBy: claims.UserID,
branchID: claims.BranchID,
partnerID: claims.PartnerID,
roleID: claims.Role,
}, nil
}

View File

@ -4,6 +4,8 @@ type Role int64
const (
SuperAdmin Role = 1
BranchAdmin Role = 2
CasheerAdmin Role = 3
Admin Role = 2
PartnerAdmin Role = 3
SiteAdmin Role = 4
Casheer Role = 5
)

View File

@ -3,8 +3,8 @@ package studio
type StudioStatus string
const (
Active StudioStatus = "Active"
Inactive StudioStatus = "Inactive"
Active StudioStatus = "active"
Inactive StudioStatus = "inactive"
)
func (b StudioStatus) toString() string {

View File

@ -24,8 +24,9 @@ type UserDB struct {
NIK string `gorm:"column:nik" json:"nik"`
RoleID int64 `gorm:"column:role_id" json:"role_id"`
RoleName string `gorm:"column:role_name" json:"role_name"`
BranchID *int64 `gorm:"column:partner_id" json:"partner_id"`
BranchName string `gorm:"column:partner_name" json:"partner_name"`
PartnerID *int64 `gorm:"column:partner_id" json:"partner_id"`
PartnerName string `gorm:"column:partner_name" json:"partner_name"`
PartnerStatus string `gorm:"column:partner_status" json:"partner_status"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at" json:"deleted_at"`
@ -47,8 +48,8 @@ func (u *UserDB) ToUser() *User {
UpdatedAt: u.UpdatedAt,
RoleID: role.Role(u.RoleID),
RoleName: u.RoleName,
PartnerID: u.BranchID,
BranchName: u.BranchName,
PartnerID: u.PartnerID,
PartnerName: u.PartnerName,
}
return userEntity
@ -63,7 +64,7 @@ func (u *UserDB) ToUserRoleDB() *UserRoleDB {
ID: 0,
UserID: u.ID,
RoleID: u.RoleID,
PartnerID: u.BranchID,
PartnerID: u.PartnerID,
CreatedAt: u.CreatedAt,
UpdatedAt: u.UpdatedAt,
}
@ -81,8 +82,9 @@ func (u *UserDB) ToUserAuthenticate(signedToken string) *AuthenticateUser {
Name: u.Name,
RoleID: role.Role(u.RoleID),
RoleName: u.RoleName,
BranchID: u.BranchID,
BranchName: u.BranchName,
PartnerID: u.PartnerID,
PartnerName: u.PartnerName,
PartnerStatus: u.PartnerStatus,
}
}
@ -116,7 +118,7 @@ func (u *UserDB) ToUpdatedUser(req User) error {
}
if *req.PartnerID > 0 {
u.BranchID = req.PartnerID
u.PartnerID = req.PartnerID
}
if req.RoleID > 0 {

View File

@ -7,6 +7,6 @@ type JWTAuthClaims struct {
Name string `json:"name"`
Email string `json:"email"`
Role int `json:"role"`
BranchID int64 `json:"branch_id"`
PartnerID int64 `json:"partner_id"`
jwt.StandardClaims
}

View File

@ -1,19 +1,42 @@
package entity
import (
"furtuna-be/internal/constants/role"
"time"
)
type CreatePartnerRequest struct {
Name string `json:"name" validate:"required"`
Address string `json:"address"`
Username string `json:"username" validate:"required"`
FullName string `json:"full_name"`
Email string `json:"email"`
Password string `json:"password" validate:"required"`
NIK string `json:"nik"`
PhoneNumber string `json:"phone_number"`
BankName string `json:"bank_name"`
BankAccountNumber string `json:"bank_account_number"`
BankAccountHolderName string `json:"bank_account_holder_name"`
}
type Partner struct {
ID int64
Name string
Status string
Address string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
CreatedBy int64
UpdatedBy int64
ID int64 `gorm:"primaryKey;autoIncrement;column:id"`
Name string `gorm:"type:varchar(255);not null;column:name"`
Status string `gorm:"type:varchar(50);column:status"`
LicenseExpiredDate *time.Time `gorm:"type:date;column:license_expired_date"`
Address string `gorm:"type:varchar(255);column:address"`
BankName string `gorm:"type:varchar(255);column:bank_name"`
BankAccountNumber string `gorm:"type:varchar(50);column:bank_account_number"`
BankAccountHolderName string `gorm:"type:varchar(255);column:bank_account_holder_name"`
CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at"`
CreatedBy int64 `gorm:"type:int;column:created_by"`
UpdatedBy int64 `gorm:"type:int;column:updated_by"`
}
func (Partner) TableName() string {
return "partners"
}
type PartnerSearch struct {
@ -80,3 +103,46 @@ func (o *PartnerDB) SetDeleted(updatedBy int64) {
o.DeletedAt = &currentTime
o.UpdatedBy = updatedBy
}
func (c *CreatePartnerRequest) ToUserAdmin(partnerID int64) *User {
return &User{
Name: c.FullName,
Password: c.Password,
Email: c.Email,
NIK: c.NIK,
Status: "active",
RoleID: role.PartnerAdmin,
PartnerID: &partnerID,
}
}
func (e *CreatePartnerRequest) ToPartnerDB(createdBy int64) *PartnerDB {
twoDays := 48 * time.Hour
licenseExpiredDate := time.Now().Add(twoDays)
return &PartnerDB{
Partner: Partner{
Name: e.Name,
Status: "inactive",
Address: e.Address,
CreatedBy: createdBy,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
BankAccountHolderName: e.BankAccountHolderName,
BankAccountNumber: e.BankAccountNumber,
BankName: e.BankName,
LicenseExpiredDate: &licenseExpiredDate,
},
}
}
func (e *CreatePartnerRequest) ToWallet(partnerID int64) *Wallet {
return &Wallet{
PartnerID: partnerID,
Balance: 0,
Currency: "IDR",
Status: "active",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}

View File

@ -6,20 +6,25 @@ import (
)
type Product struct {
ID int64
Name string
Type product.ProductType
Price float64
Status product.ProductStatus
Description string
Image string
BranchID int64
StockQty int64
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
CreatedBy int64
UpdatedBy int64
ID int64 `gorm:"primaryKey;autoIncrement;column:id"`
PartnerID int64 `gorm:"type:int;column:partner_id"`
SiteID int64 `gorm:"type:int;column:site_id"`
Name string `gorm:"type:varchar(255);not null;column:name"`
Type string `gorm:"type:varchar;column:type"`
Price float64 `gorm:"type:decimal;column:price"`
IsWeekendTicket bool `gorm:"type:bool;column:is_weekend_ticket"`
IsSeasonTicket bool `gorm:"type:bool;column:is_season_ticket"`
Status string `gorm:"type:varchar;column:status"`
Description string `gorm:"type:varchar(255);not null;column:description"`
CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at"`
CreatedBy int64 `gorm:"type:int;column:created_by"`
UpdatedBy int64 `gorm:"type:int;column:updated_by"`
}
func (Product) TableName() string {
return "products"
}
type ProductSearch struct {
@ -56,9 +61,7 @@ func (e *ProductDB) ToProduct() *Product {
Price: e.Price,
Status: e.Status,
Description: e.Description,
Image: e.Image,
BranchID: e.BranchID,
StockQty: e.StockQty,
PartnerID: e.PartnerID,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
DeletedAt: e.DeletedAt,
@ -97,14 +100,6 @@ func (o *ProductDB) ToUpdatedProduct(updatedby int64, req Product) {
if req.Description != "" {
o.Description = req.Description
}
if req.Image != "" {
o.Image = req.Image
}
if req.StockQty > 0 {
o.StockQty = req.StockQty
}
}
func (o *ProductDB) SetDeleted(updatedby int64) {

145
internal/entity/sites.go Normal file
View File

@ -0,0 +1,145 @@
package entity
import (
"time"
)
type Site struct {
ID int64 `gorm:"primaryKey;autoIncrement;column:id"`
Name string `gorm:"type:varchar(255);not null;column:name"`
PartnerID int64 `gorm:"type:int;column:partner_id"`
Image string `gorm:"type:varchar;column:image"`
Address string `gorm:"type:varchar;column:address"`
LocationLink string `gorm:"type:varchar;column:location_link"`
Description string `gorm:"type:varchar;column:description"`
Highlight string `gorm:"type:varchar;column:highlight"`
ContactPerson string `gorm:"type:varchar;column:contact_person"`
TnC string `gorm:"type:varchar;column:tnc"`
AdditionalInfo string `gorm:"type:varchar;column:additional_info"`
Status string `gorm:"type:varchar;column:status"`
IsSeasonTicket bool `gorm:"type:bool;column:is_season_ticket"`
IsDiscountActive bool `gorm:"type:bool;column:is_discount_active"`
CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at"`
CreatedBy int64 `gorm:"type:int;column:created_by"`
UpdatedBy int64 `gorm:"type:int;column:updated_by"`
Products []Product `gorm:"foreignKey:SiteID;constraint:OnDelete:CASCADE;"`
}
type SiteSearch struct {
Search string
Name string
Limit int
Offset int
}
type SiteList []*SiteDB
type SiteDB struct {
Site
}
func (s *Site) ToSiteDB() *SiteDB {
return &SiteDB{
Site: *s,
}
}
func (SiteDB) TableName() string {
return "sites"
}
func (e *SiteDB) ToSite() *Site {
return &Site{
ID: e.ID,
Name: e.Name,
PartnerID: e.PartnerID,
Image: e.Image,
Address: e.Address,
LocationLink: e.LocationLink,
Description: e.Description,
Highlight: e.Highlight,
ContactPerson: e.ContactPerson,
TnC: e.TnC,
AdditionalInfo: e.AdditionalInfo,
Status: e.Status,
IsSeasonTicket: e.IsSeasonTicket,
IsDiscountActive: e.IsDiscountActive,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
DeletedAt: e.DeletedAt,
CreatedBy: e.CreatedBy,
UpdatedBy: e.UpdatedBy,
}
}
func (s *SiteList) ToSiteList() []*Site {
var sites []*Site
for _, site := range *s {
sites = append(sites, site.ToSite())
}
return sites
}
func (o *SiteDB) ToUpdatedSite(updatedBy int64, req Site) {
o.UpdatedBy = updatedBy
if req.Name != "" {
o.Name = req.Name
}
if req.PartnerID != 0 {
o.PartnerID = req.PartnerID
}
if req.Image != "" {
o.Image = req.Image
}
if req.Address != "" {
o.Address = req.Address
}
if req.LocationLink != "" {
o.LocationLink = req.LocationLink
}
if req.Description != "" {
o.Description = req.Description
}
if req.Highlight != "" {
o.Highlight = req.Highlight
}
if req.ContactPerson != "" {
o.ContactPerson = req.ContactPerson
}
if req.TnC != "" {
o.TnC = req.TnC
}
if req.AdditionalInfo != "" {
o.AdditionalInfo = req.AdditionalInfo
}
if req.Status != "" {
o.Status = req.Status
}
if req.IsSeasonTicket {
o.IsSeasonTicket = req.IsSeasonTicket
}
if req.IsDiscountActive {
o.IsDiscountActive = req.IsDiscountActive
}
}
func (o *SiteDB) SetDeleted(updatedBy int64) {
currentTime := time.Now()
o.DeletedAt = &currentTime
o.UpdatedBy = updatedBy
}

View File

@ -21,7 +21,7 @@ type User struct {
RoleID role.Role
RoleName string
PartnerID *int64
BranchName string
PartnerName string
}
type AuthenticateUser struct {
@ -29,8 +29,9 @@ type AuthenticateUser struct {
Name string
RoleID role.Role
RoleName string
BranchID *int64
BranchName string
PartnerID *int64
PartnerName string
PartnerStatus string
}
type UserRoleDB struct {
@ -53,7 +54,7 @@ func (u *User) ToUserDB(createdBy int64) (*UserDB, error) {
return nil, err
}
if u.RoleID == role.BranchAdmin && u.PartnerID == nil {
if u.RoleID == role.Admin && u.PartnerID == nil {
return nil, errors.New("invalid request")
}
@ -62,7 +63,7 @@ func (u *User) ToUserDB(createdBy int64) (*UserDB, error) {
Email: u.Email,
Password: hashedPassword,
RoleID: int64(u.RoleID),
BranchID: u.PartnerID,
PartnerID: u.PartnerID,
Status: userstatus.Active,
CreatedBy: createdBy,
}, nil

17
internal/entity/wallet.go Normal file
View File

@ -0,0 +1,17 @@
package entity
import "time"
type Wallet struct {
ID int64 `gorm:"primaryKey;autoIncrement;column:id"`
PartnerID int64 `gorm:"type:int;not null;column:partner_id"`
Balance float64 `gorm:"type:decimal(18,2);not null;default:0.00;column:balance"`
Currency string `gorm:"type:varchar(3);not null;column:currency"`
Status string `gorm:"type:varchar(50);column:status"`
CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
}
func (Wallet) TableName() string {
return "wallets"
}

View File

@ -51,18 +51,19 @@ func (h *AuthHandler) AuthLogin(c *gin.Context) {
return
}
var branch *response.Branch
var partner *response.Partner
if authUser.RoleID != role.SuperAdmin {
branch = &response.Branch{
ID: authUser.BranchID,
Name: authUser.BranchName,
partner = &response.Partner{
ID: authUser.PartnerID,
Name: authUser.PartnerName,
Status: authUser.PartnerStatus,
}
}
resp := response.LoginResponse{
Token: authUser.Token,
Branch: branch,
Partner: partner,
Name: authUser.Name,
Role: response.Role{
ID: int64(authUser.RoleID),

View File

@ -51,7 +51,7 @@ func NewHandler(service services.Partner) *Handler {
func (h *Handler) Create(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.Partner
var req request.CreatePartnerRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return

View File

@ -246,9 +246,6 @@ func (h *Handler) toProductResponse(resp *entity.Product) response.Product {
Price: resp.Price,
Status: resp.Status,
Description: resp.Description,
Image: resp.Image,
BranchID: resp.BranchID,
StockQty: resp.StockQty,
CreatedAt: resp.CreatedAt.Format(time.RFC3339),
UpdatedAt: resp.CreatedAt.Format(time.RFC3339),
}

View File

@ -0,0 +1,299 @@
package site
import (
"furtuna-be/internal/common/errors"
"furtuna-be/internal/entity"
"furtuna-be/internal/handlers/request"
"furtuna-be/internal/handlers/response"
"furtuna-be/internal/services"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
type Handler struct {
service services.Site
}
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/site")
route.POST("/", jwt, h.Create)
route.GET("/list", jwt, h.GetAll)
route.PUT("/:id", jwt, h.Update)
route.GET("/:id", jwt, h.GetByID)
route.DELETE("/:id", jwt, h.Delete)
}
func NewHandler(service services.Site) *Handler {
return &Handler{
service: service,
}
}
// Create handles the creation of a new Site.
// @Summary Create a new Site
// @Description Create a new Site based on the provided data.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param req body request.Site true "New Site details"
// @Success 200 {object} response.BaseResponse{data=response.Site} "Site created successfully"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/site [post]
// @Tags Site APIs
func (h *Handler) Create(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.Site
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if !ctx.IsSuperAdmin() {
req.PartnerID = ctx.GetPartnerID()
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
res, err := h.service.Create(ctx, req.ToEntity(ctx.RequestedBy()))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toSiteResponse(res),
})
}
// Update handles the update of an existing Site.
// @Summary Update an existing Site
// @Description Update the details of an existing Site based on the provided ID.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param id path int64 true "Site ID to update"
// @Param req body request.Site true "Updated Site details"
// @Success 200 {object} response.BaseResponse{data=response.Site} "Site updated successfully"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/site/{id} [put]
// @Tags Site APIs
func (h *Handler) Update(c *gin.Context) {
ctx := request.GetMyContext(c)
id := c.Param("id")
SiteID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
var req request.Site
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
}
updatedSite, err := h.service.Update(ctx, SiteID, req.ToEntity(ctx.RequestedBy()))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toSiteResponse(updatedSite),
})
}
// GetAll retrieves a list of Sites.
// @Summary Get a list of Sites
// @Description Get a paginated list of Sites based on query parameters.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param Limit query int false "Number of items to retrieve (default 10)"
// @Param Offset query int false "Offset for pagination (default 0)"
// @Success 200 {object} response.BaseResponse{data=response.SiteList} "List of Sites"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/site/list [get]
// @Tags Site APIs
func (h *Handler) GetAll(c *gin.Context) {
var req request.SiteParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
Sites, total, err := h.service.GetAll(c.Request.Context(), req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toSiteResponseList(Sites, int64(total), req),
})
}
// Delete handles the deletion of a Site by ID.
// @Summary Delete a Site by ID
// @Description Delete a Site based on the provided ID.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param id path int64 true "Site ID to delete"
// @Success 200 {object} response.BaseResponse "Site deleted successfully"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/site/{id} [delete]
// @Tags Site APIs
func (h *Handler) Delete(c *gin.Context) {
ctx := request.GetMyContext(c)
id := c.Param("id")
// Parse the ID into a uint
SiteID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
err = h.service.Delete(ctx, SiteID)
if err != nil {
c.JSON(http.StatusInternalServerError, response.BaseResponse{
Success: false,
Status: http.StatusInternalServerError,
Message: err.Error(),
Data: nil,
})
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: nil,
})
}
// GetByID retrieves details of a specific Site by ID.
// @Summary Get details of a Site by ID
// @Description Get details of a Site based on the provided ID.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param id path int64 true "Site ID to retrieve"
// @Success 200 {object} response.BaseResponse{data=response.Site} "Site details"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/site/{id} [get]
// @Tags Site APIs
func (h *Handler) GetByID(c *gin.Context) {
id := c.Param("id")
// Parse the ID into a uint
SiteID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, err := h.service.GetByID(c.Request.Context(), SiteID)
if err != nil {
c.JSON(http.StatusInternalServerError, response.BaseResponse{
Success: false,
Status: http.StatusInternalServerError,
Message: err.Error(),
Data: nil,
})
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toSiteResponse(res),
})
}
func (h *Handler) toSiteResponse(resp *entity.Site) response.Site {
return response.Site{
ID: &resp.ID,
Name: resp.Name,
PartnerID: resp.PartnerID,
Image: resp.Image,
Address: resp.Address,
LocationLink: resp.LocationLink,
Description: resp.Description,
Highlight: resp.Highlight,
ContactPerson: resp.ContactPerson,
TnC: resp.TnC,
AdditionalInfo: resp.AdditionalInfo,
Status: resp.Status,
IsSeasonTicket: resp.IsSeasonTicket,
IsDiscountActive: resp.IsDiscountActive,
CreatedAt: resp.CreatedAt.Format(time.RFC3339),
UpdatedAt: resp.UpdatedAt.Format(time.RFC3339),
}
}
func (h *Handler) toSiteResponseList(resp []*entity.Site, total int64, req request.SiteParam) response.SiteList {
var Sites []response.Site
for _, b := range resp {
Sites = append(Sites, h.toSiteResponse(b))
}
return response.SiteList{
Sites: Sites,
Total: total,
Limit: req.Limit,
Offset: req.Offset,
}
}
func (h *Handler) toProductResponseList(products []entity.Product) []response.Product {
var res []response.Product
for _, product := range products {
res = append(res, response.Product{
ID: product.ID,
PartnerID: product.PartnerID,
SiteID: product.SiteID,
Name: product.Name,
Type: product.Type,
Price: product.Price,
IsWeekendTicket: product.IsWeekendTicket,
IsSeasonTicket: product.IsSeasonTicket,
Status: product.Status,
Description: product.Description,
CreatedAt: product.CreatedAt.Format(time.RFC3339),
UpdatedAt: product.UpdatedAt.Format(time.RFC3339),
})
}
return res
}

View File

@ -56,13 +56,15 @@ func (h *Handler) Create(c *gin.Context) {
return
}
req.PartnerID = ctx.GetPartnerID()
if err := req.Validate(); err != nil {
response.ErrorWrapper(c, errors.ErrorInvalidRequest)
return
}
res, err := h.service.Create(ctx, req.ToEntity())
ctx.IsSuperAdmin()
res, err := h.service.Create(ctx, req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
@ -267,7 +269,7 @@ func (h *Handler) toUserResponse(resp *entity.User) response.User {
RoleID: int64(resp.RoleID),
RoleName: resp.RoleName,
PartnerID: resp.PartnerID,
BranchName: resp.BranchName,
PartnerName: resp.PartnerName,
CreatedAt: resp.CreatedAt.Format(time.RFC3339),
UpdatedAt: resp.CreatedAt.Format(time.RFC3339),
}

View File

@ -26,6 +26,36 @@ type Partner struct {
Status string `json:"status"`
}
type CreatePartnerRequest struct {
Name string `json:"name" validate:"required"`
Address string `json:"address"`
Username string `json:"username" validate:"required"`
FullName string `json:"full_name"`
Email string `json:"email"`
Password string `json:"password" validate:"required"`
NIK string `json:"nik"`
PhoneNumber string `json:"phone_number"`
BankName string `json:"bank_name"`
BankAccountNumber string `json:"bank_account_number"`
BankAccountHolderName string `json:"bank_account_holder_name"`
}
func (e *CreatePartnerRequest) ToEntity() *entity.CreatePartnerRequest {
return &entity.CreatePartnerRequest{
Name: e.Name,
Address: e.Address,
Username: e.Username,
FullName: e.FullName,
Email: e.Email,
Password: e.Password,
NIK: e.NIK,
PhoneNumber: e.PhoneNumber,
BankName: e.BankName,
BankAccountNumber: e.BankAccountNumber,
BankAccountHolderName: e.BankAccountHolderName,
}
}
func (e *Partner) ToEntity() *entity.Partner {
return &entity.Partner{
Name: e.Name,

View File

@ -28,14 +28,15 @@ func (p *ProductParam) ToEntity() entity.ProductSearch {
}
type Product struct {
ID int64 `json:"id,omitempty"`
PartnerID int64 `json:"partner_id"`
Name string `json:"name" validate:"required"`
Type product.ProductType `json:"type" validate:"required"`
Type string `json:"type"`
Price float64 `json:"price" validate:"required"`
Status product.ProductStatus `json:"status" validate:"required"`
Description string `json:"description" `
Image string `json:"image" `
BranchID int64 `json:"branch_id" validate:"required"`
StockQty int64 `json:"stock_qty" `
IsWeekendTicket bool `json:"is_weekend_ticket"`
IsSeasonTicket bool `json:"is_season_ticket"`
Status string `json:"status"`
Description string `json:"description" validate:"required"`
}
func (e *Product) ToEntity() *entity.Product {
@ -45,8 +46,5 @@ func (e *Product) ToEntity() *entity.Product {
Price: e.Price,
Status: e.Status,
Description: e.Description,
Image: e.Image,
BranchID: e.BranchID,
StockQty: e.StockQty,
}
}

View File

@ -0,0 +1,75 @@
package request
import (
"furtuna-be/internal/entity"
)
type Site struct {
ID int64 `json:"id"`
Name string `json:"name" validate:"required"`
PartnerID *int64 `json:"partner_id"`
Image string `json:"image"`
Address string `json:"address"`
LocationLink string `json:"location_link"`
Description string `json:"description"`
Highlight string `json:"highlight"`
ContactPerson string `json:"contact_person"`
TnC string `json:"tnc"`
AdditionalInfo string `json:"additional_info"`
Status string `json:"status"`
IsSeasonTicket bool `json:"is_season_ticket"`
IsDiscountActive bool `json:"is_discount_active"`
Products []Product `json:"products"`
}
func (r *Site) ToEntity(createdBy int64) *entity.Site {
var products []entity.Product
for _, p := range r.Products {
products = append(products, entity.Product{
ID: p.ID,
PartnerID: *r.PartnerID,
Name: p.Name,
Type: p.Type,
Price: p.Price,
IsWeekendTicket: p.IsWeekendTicket,
IsSeasonTicket: p.IsSeasonTicket,
Status: p.Status,
Description: p.Description,
CreatedBy: createdBy,
})
}
return &entity.Site{
ID: r.ID,
Name: r.Name,
PartnerID: *r.PartnerID,
Image: r.Image,
Address: r.Address,
LocationLink: r.LocationLink,
Description: r.Description,
Highlight: r.Highlight,
ContactPerson: r.ContactPerson,
TnC: r.TnC,
AdditionalInfo: r.AdditionalInfo,
Status: r.Status,
IsSeasonTicket: r.IsSeasonTicket,
IsDiscountActive: r.IsDiscountActive,
Products: products,
}
}
type SiteParam struct {
Search string `form:"search"`
Name string `form:"name"`
Limit int `form:"limit,default=10"`
Offset int `form:"offset,default=0"`
}
func (r *SiteParam) ToEntity() entity.SiteSearch {
return entity.SiteSearch{
Search: r.Search,
Name: r.Name,
Limit: r.Limit,
Offset: r.Offset,
}
}

View File

@ -4,7 +4,7 @@ type LoginResponse struct {
Token string `json:"token"`
Name string `json:"name"`
Role Role `json:"role"`
Branch *Branch `json:"branch"`
Partner *Partner `json:"partner"`
}
type Role struct {

View File

@ -4,9 +4,9 @@ type Partner struct {
ID *int64 `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
Address string `json:"address"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Address string `json:"address,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
type PartnerList struct {

View File

@ -1,17 +1,16 @@
package response
import "furtuna-be/internal/constants/product"
type Product struct {
ID int64 `json:"id"`
PartnerID int64 `json:"partner_id"`
SiteID int64 `json:"site_id"`
Name string `json:"name"`
Type product.ProductType `json:"type"`
Type string `json:"type"`
Price float64 `json:"price"`
Status product.ProductStatus `json:"status"`
Description string `json:"description" `
Image string `json:"image" `
BranchID int64 `json:"branch_id"`
StockQty int64 `json:"stock_qty"`
IsWeekendTicket bool `json:"is_weekend_ticket"`
IsSeasonTicket bool `json:"is_season_ticket"`
Status string `json:"status"`
Description string `json:"description"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}

View File

@ -0,0 +1,27 @@
package response
type Site struct {
ID *int64 `json:"id"`
Name string `json:"name"`
PartnerID int64 `json:"partner_id"`
Image string `json:"image"`
Address string `json:"address"`
LocationLink string `json:"location_link"`
Description string `json:"description"`
Highlight string `json:"highlight"`
ContactPerson string `json:"contact_person"`
TnC string `json:"tnc"`
AdditionalInfo string `json:"additional_info"`
Status string `json:"status"`
IsSeasonTicket bool `json:"is_season_ticket"`
IsDiscountActive bool `json:"is_discount_active"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
type SiteList struct {
Sites []Site `json:"sites"`
Total int64 `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}

View File

@ -8,7 +8,7 @@ type User struct {
RoleID int64 `json:"role_id"`
RoleName string `json:"role_name"`
PartnerID *int64 `json:"partner_id"`
BranchName string `json:"partner_name"`
PartnerName string `json:"partner_name"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}

View File

@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"go.uber.org/zap"
"gorm.io/gorm"
@ -27,13 +26,17 @@ func (r *AuthRepository) CheckExistsUserAccount(ctx context.Context, email strin
err := r.db.
Table("users").
Select("users.*, user_roles.role_id, user_roles.partner_id, roles.role_name, partners.name as partner_name").
Select("users.*, user_roles.role_id, user_roles.partner_id, roles.role_name, partners.name as partner_name, partners.status as partner_status").
Where("users.email = ?", email).
Joins("left join user_roles on users.id = user_roles.user_id").
Joins("left join roles on user_roles.role_id = roles.role_id").
Joins("left join partners on user_roles.partner_id = partners.id").
First(&user).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user with email %s does not exist", email) // or use a custom error type

View File

@ -44,9 +44,9 @@ func (c *CryptoImpl) ValidateWT(tokenString string) (*jwt.Token, error) {
}
func (c *CryptoImpl) GenerateJWT(user *entity.User) (string, error) {
branchID := int64(0)
partnerID := int64(0)
if user.PartnerID != nil {
branchID = *user.PartnerID
partnerID = *user.PartnerID
}
claims := &entity.JWTAuthClaims{
@ -60,7 +60,7 @@ func (c *CryptoImpl) GenerateJWT(user *entity.User) (string, error) {
Name: user.Name,
Email: user.Email,
Role: int(user.RoleID),
BranchID: branchID,
PartnerID: partnerID,
}
token, err := jwt.

View File

@ -28,6 +28,15 @@ func (b *PartnerRepository) Create(ctx context.Context, Partner *entity.PartnerD
return Partner, nil
}
func (b *PartnerRepository) CreateWithTx(ctx context.Context, tx *gorm.DB, Partner *entity.PartnerDB) (*entity.PartnerDB, error) {
err := tx.Create(Partner).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create Partner", zap.Error(err))
return nil, err
}
return Partner, nil
}
func (b *PartnerRepository) Update(ctx context.Context, Partner *entity.PartnerDB) (*entity.PartnerDB, error) {
if err := b.db.Save(Partner).Error; err != nil {
logger.ContextLogger(ctx).Error("error when update Partner", zap.Error(err))

View File

@ -2,14 +2,17 @@ package repository
import (
"context"
"database/sql"
"furtuna-be/internal/repository/branches"
"furtuna-be/internal/repository/orders"
"furtuna-be/internal/repository/oss"
"furtuna-be/internal/repository/partners"
"furtuna-be/internal/repository/products"
"furtuna-be/internal/repository/sites"
"furtuna-be/internal/repository/studios"
"furtuna-be/internal/repository/trx"
"furtuna-be/internal/repository/users"
repository "furtuna-be/internal/repository/wallet"
"github.com/golang-jwt/jwt"
"gorm.io/gorm"
@ -31,6 +34,9 @@ type RepoManagerImpl struct {
Order Order
OSS OSSRepository
Partner PartnerRepository
Site SiteRepository
Trx TransactionManager
Wallet WalletRepository
}
func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
@ -45,6 +51,9 @@ func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
Order: orders.NewOrderRepository(db),
OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig),
Partner: partners.NewPartnerRepository(db),
Site: sites.NewSiteRepository(db),
Trx: trx.NewGormTransactionManager(db),
Wallet: repository.NewWalletRepository(db),
}
}
@ -69,6 +78,7 @@ type Crypto interface {
type User interface {
Create(ctx context.Context, user *entity.UserDB) (*entity.UserDB, error)
CreateWithTx(ctx context.Context, tx *gorm.DB, user *entity.UserDB) (*entity.UserDB, error)
GetAllUsers(ctx context.Context, req entity.UserSearch) (entity.UserList, int, error)
GetUserByID(ctx context.Context, id int64) (*entity.UserDB, error)
GetUserByEmail(ctx context.Context, email string) (*entity.UserDB, error)
@ -115,8 +125,28 @@ type OSSRepository interface {
type PartnerRepository interface {
Create(ctx context.Context, branch *entity.PartnerDB) (*entity.PartnerDB, error)
CreateWithTx(ctx context.Context, tx *gorm.DB, Partner *entity.PartnerDB) (*entity.PartnerDB, error)
Update(ctx context.Context, branch *entity.PartnerDB) (*entity.PartnerDB, error)
GetByID(ctx context.Context, id int64) (*entity.PartnerDB, error)
GetAll(ctx context.Context, req entity.PartnerSearch) (entity.PartnerList, int, error)
Delete(ctx context.Context, id int64) error
}
type SiteRepository interface {
Upsert(ctx context.Context, site *entity.Site) (*entity.Site, error)
Create(ctx context.Context, branch *entity.SiteDB) (*entity.SiteDB, error)
Update(ctx context.Context, branch *entity.SiteDB) (*entity.SiteDB, error)
GetByID(ctx context.Context, id int64) (*entity.SiteDB, error)
GetAll(ctx context.Context, req entity.SiteSearch) (entity.SiteList, int, error)
Delete(ctx context.Context, id int64) error
}
type TransactionManager interface {
Begin(ctx context.Context, opts ...*sql.TxOptions) (*gorm.DB, error)
Commit(session *gorm.DB) *gorm.DB
Rollback(session *gorm.DB) *gorm.DB
}
type WalletRepository interface {
Create(ctx context.Context, tx *gorm.DB, wallet *entity.Wallet) (*entity.Wallet, error)
}

View File

@ -0,0 +1,131 @@
package sites
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type SiteRepository struct {
db *gorm.DB
}
func NewSiteRepository(db *gorm.DB) *SiteRepository {
return &SiteRepository{
db: db,
}
}
func (r *SiteRepository) Upsert(ctx context.Context, site *entity.Site) (*entity.Site, error) {
err := r.db.Transaction(func(tx *gorm.DB) error {
if site.ID != 0 {
// Update site
if err := tx.Save(site).Error; err != nil {
return err
}
} else {
// Create new site
if err := tx.Create(site).Error; err != nil {
return err
}
}
if len(site.Products) > 0 {
for i := range site.Products {
site.Products[i].SiteID = site.ID
if site.Products[i].ID != 0 {
// Update existing product
if err := tx.Save(&site.Products[i]).Error; err != nil {
return err
}
} else {
// Create new product
if err := tx.Create(&site.Products[i]).Error; err != nil {
return err
}
}
}
}
return nil
})
if err != nil {
logger.ContextLogger(ctx).Error("error when upserting site", zap.Error(err))
return nil, err
}
return site, nil
}
func (r *SiteRepository) Create(ctx context.Context, site *entity.SiteDB) (*entity.SiteDB, error) {
err := r.db.Create(site).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when creating site", zap.Error(err))
return nil, err
}
return site, nil
}
func (r *SiteRepository) Update(ctx context.Context, site *entity.SiteDB) (*entity.SiteDB, error) {
if err := r.db.Save(site).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating site", zap.Error(err))
return nil, err
}
return site, nil
}
func (r *SiteRepository) GetByID(ctx context.Context, id int64) (*entity.SiteDB, error) {
site := new(entity.SiteDB)
if err := r.db.First(site, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting site by ID", zap.Error(err))
return nil, err
}
return site, nil
}
func (r *SiteRepository) GetAll(ctx context.Context, req entity.SiteSearch) (entity.SiteList, int, error) {
var sites []*entity.SiteDB
var total int64
query := r.db
query = query.Where("deleted_at IS NULL")
if req.Search != "" {
query = query.Where("name ILIKE ?", "%"+req.Search+"%")
}
if req.Name != "" {
query = query.Where("name ILIKE ?", "%"+req.Name+"%")
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
if err := query.Find(&sites).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting all sites", zap.Error(err))
return nil, 0, err
}
if err := r.db.Model(&entity.SiteDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when counting sites", zap.Error(err))
return nil, 0, err
}
return sites, int(total), nil
}
func (r *SiteRepository) Delete(ctx context.Context, id int64) error {
site := new(entity.SiteDB)
site.ID = id
if err := r.db.Delete(site).Error; err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,33 @@
package trx
import (
"context"
"database/sql"
"gorm.io/gorm"
)
type GormTransactionManager struct {
db *gorm.DB
}
func NewGormTransactionManager(db *gorm.DB) *GormTransactionManager {
return &GormTransactionManager{
db: db,
}
}
func (tm *GormTransactionManager) Begin(ctx context.Context, opts ...*sql.TxOptions) (*gorm.DB, error) {
tx := tm.db.Begin(opts...)
if tx.Error != nil {
return nil, tx.Error
}
return tx, nil
}
func (tm *GormTransactionManager) Commit(tx *gorm.DB) *gorm.DB {
return tx.Commit()
}
func (tm *GormTransactionManager) Rollback(tx *gorm.DB) *gorm.DB {
return tx.Rollback()
}

View File

@ -51,6 +51,29 @@ func (r *UserRepository) Create(ctx context.Context, user *entity.UserDB) (*enti
return user, nil
}
func (r *UserRepository) CreateWithTx(ctx context.Context, tx *gorm.DB, user *entity.UserDB) (*entity.UserDB, error) {
user.ID = 0
if err := tx.Select("name", "email", "password", "status", "created_by", "nik", "user_type", "phone_number").Create(user).Error; err != nil {
logError(ctx, "creating user", err)
return nil, err
}
if err := tx.First(user, user.ID).Error; err != nil {
tx.Rollback()
logError(ctx, "retrieving user", err)
return nil, err
}
userRole := user.ToUserRoleDB()
if err := tx.Create(userRole).Error; err != nil {
tx.Rollback()
logError(ctx, "creating user role", err)
return nil, err
}
return user, nil
}
func (b *UserRepository) GetAllUsers(ctx context.Context, req entity.UserSearch) (entity.UserList, int, error) {
var users []*entity.UserDB
var total int64

View File

@ -0,0 +1,65 @@
package repository
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type WalletRepository struct {
db *gorm.DB
}
func NewWalletRepository(db *gorm.DB) *WalletRepository {
return &WalletRepository{
db: db,
}
}
func (r *WalletRepository) Create(ctx context.Context, tx *gorm.DB, wallet *entity.Wallet) (*entity.Wallet, error) {
err := tx.Create(wallet).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when creating wallet", zap.Error(err))
return nil, err
}
return wallet, nil
}
func (r *WalletRepository) Update(ctx context.Context, wallet *entity.Wallet) (*entity.Wallet, error) {
if err := r.db.Save(wallet).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating wallet", zap.Error(err))
return nil, err
}
return wallet, nil
}
func (r *WalletRepository) GetByID(ctx context.Context, id int64) (*entity.Wallet, error) {
wallet := new(entity.Wallet)
if err := r.db.First(wallet, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting wallet by id", zap.Error(err))
return nil, err
}
return wallet, nil
}
func (r *WalletRepository) GetAll(ctx context.Context) ([]*entity.Wallet, error) {
var wallets []*entity.Wallet
if err := r.db.Find(&wallets).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting all wallets", zap.Error(err))
return nil, err
}
return wallets, nil
}
func (r *WalletRepository) Delete(ctx context.Context, id int64) error {
wallet := new(entity.Wallet)
wallet.ID = id
if err := r.db.Delete(wallet).Error; err != nil {
logger.ContextLogger(ctx).Error("error when deleting wallet", zap.Error(err))
return err
}
return nil
}

View File

@ -6,6 +6,7 @@ import (
"furtuna-be/internal/handlers/http/oss"
"furtuna-be/internal/handlers/http/partner"
"furtuna-be/internal/handlers/http/product"
site "furtuna-be/internal/handlers/http/sites"
"furtuna-be/internal/handlers/http/studio"
"furtuna-be/internal/handlers/http/user"
swaggerFiles "github.com/swaggo/files"
@ -52,6 +53,7 @@ func RegisterPrivateRoutes(app *app.Server, serviceManager *services.ServiceMana
order.NewHandler(serviceManager.OrderSvc),
oss.NewOssHandler(serviceManager.OSSSvc),
partner.NewHandler(serviceManager.PartnerSvc),
site.NewHandler(serviceManager.SiteSvc),
}
for _, handler := range serverRoutes {

View File

@ -6,31 +6,68 @@ import (
"furtuna-be/internal/common/mycontext"
"furtuna-be/internal/entity"
"furtuna-be/internal/repository"
"furtuna-be/internal/services/users"
"go.uber.org/zap"
)
type PartnerService struct {
repo repository.PartnerRepository
trx repository.TransactionManager
userSvc *users.UserService
walletRepo repository.WalletRepository
}
func NewPartnerService(repo repository.PartnerRepository) *PartnerService {
func NewPartnerService(repo repository.PartnerRepository,
userSvc *users.UserService, repoManager repository.TransactionManager,
walletRepo repository.WalletRepository) *PartnerService {
return &PartnerService{
repo: repo,
userSvc: userSvc,
trx: repoManager,
walletRepo: walletRepo,
}
}
func (s *PartnerService) Create(ctx mycontext.Context, PartnerReq *entity.Partner) (*entity.Partner, error) {
PartnerDB := PartnerReq.ToPartnerDB()
PartnerDB.CreatedBy = ctx.RequestedBy()
func (s *PartnerService) Create(ctx mycontext.Context, partnerReq *entity.CreatePartnerRequest) (*entity.Partner, error) {
var err error
PartnerDB, err := s.repo.Create(ctx, PartnerDB)
tx, err := s.trx.Begin(ctx)
if err != nil {
logger.ContextLogger(ctx).Error("error when create Partner", zap.Error(err))
logger.ContextLogger(ctx).Error("error when starting transaction", zap.Error(err))
return nil, err
}
return PartnerDB.ToPartner(), nil
defer func() {
if r := recover(); r != nil {
s.trx.Rollback(tx)
panic(r)
} else if err != nil {
s.trx.Rollback(tx)
} else {
err = s.trx.Commit(tx).Error
}
}()
// Create Partner
partnerDB := partnerReq.ToPartnerDB(ctx.RequestedBy())
if partnerDB, err = s.repo.CreateWithTx(ctx, tx, partnerDB); err != nil {
logger.ContextLogger(ctx).Error("error when creating partner", zap.Error(err))
return nil, err
}
adminUser := partnerReq.ToUserAdmin(partnerDB.ID)
if adminUser, err = s.userSvc.CreateWithTx(ctx, tx, adminUser); err != nil {
logger.ContextLogger(ctx).Error("error when creating admin user", zap.Error(err))
return nil, err
}
partnerWallet := partnerReq.ToWallet(partnerDB.ID)
if partnerWallet, err = s.walletRepo.Create(ctx, tx, partnerWallet); err != nil {
logger.ContextLogger(ctx).Error("error when creating wallet", zap.Error(err))
return nil, err
}
return partnerDB.ToPartner(), nil
}
func (s *PartnerService) Update(ctx mycontext.Context, id int64, PartnerReq *entity.Partner) (*entity.Partner, error) {

View File

@ -8,8 +8,10 @@ import (
"furtuna-be/internal/services/oss"
"furtuna-be/internal/services/partner"
"furtuna-be/internal/services/product"
"furtuna-be/internal/services/sites"
"furtuna-be/internal/services/studio"
"furtuna-be/internal/services/users"
"gorm.io/gorm"
"furtuna-be/config"
"furtuna-be/internal/entity"
@ -28,6 +30,7 @@ type ServiceManagerImpl struct {
OrderSvc Order
OSSSvc OSSService
PartnerSvc Partner
SiteSvc Site
}
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
@ -40,7 +43,9 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
ProductSvc: product.NewProductService(repo.Product),
OrderSvc: order.NewOrderService(repo.Order, repo.Product, repo.Studio),
OSSSvc: oss.NewOSSService(repo.OSS),
PartnerSvc: partner.NewPartnerService(repo.Partner),
PartnerSvc: partner.NewPartnerService(
repo.Partner, users.NewUserService(repo.User, repo.Branch), repo.Trx, repo.Wallet),
SiteSvc: site.NewSiteService(repo.Site),
}
}
@ -58,6 +63,7 @@ type Event interface {
type User interface {
Create(ctx mycontext.Context, userReq *entity.User) (*entity.User, error)
CreateWithTx(ctx mycontext.Context, tx *gorm.DB, userReq *entity.User) (*entity.User, error)
GetAll(ctx mycontext.Context, search entity.UserSearch) ([]*entity.User, int, error)
Update(ctx mycontext.Context, id int64, userReq *entity.User) (*entity.User, error)
GetByID(ctx mycontext.Context, id int64) (*entity.User, error)
@ -102,9 +108,17 @@ type OSSService interface {
}
type Partner interface {
Create(ctx mycontext.Context, branchReq *entity.Partner) (*entity.Partner, error)
Create(ctx mycontext.Context, branchReq *entity.CreatePartnerRequest) (*entity.Partner, error)
Update(ctx mycontext.Context, id int64, branchReq *entity.Partner) (*entity.Partner, error)
GetByID(ctx context.Context, id int64) (*entity.Partner, error)
GetAll(ctx context.Context, search entity.PartnerSearch) ([]*entity.Partner, int, error)
Delete(ctx mycontext.Context, id int64) error
}
type Site interface {
Create(ctx mycontext.Context, branchReq *entity.Site) (*entity.Site, error)
Update(ctx mycontext.Context, id int64, branchReq *entity.Site) (*entity.Site, error)
GetByID(ctx context.Context, id int64) (*entity.Site, error)
GetAll(ctx context.Context, search entity.SiteSearch) ([]*entity.Site, int, error)
Delete(ctx mycontext.Context, id int64) error
}

View File

@ -0,0 +1,89 @@
package site
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 SiteService struct {
repo repository.SiteRepository
}
func NewSiteService(repo repository.SiteRepository) *SiteService {
return &SiteService{
repo: repo,
}
}
func (s *SiteService) Create(ctx mycontext.Context, siteReq *entity.Site) (*entity.Site, error) {
siteDB := siteReq
siteDB.CreatedBy = ctx.RequestedBy()
siteDB, err := s.repo.Upsert(ctx, siteDB)
if err != nil {
logger.ContextLogger(ctx).Error("error when creating site", zap.Error(err))
return nil, err
}
return siteDB, nil
}
func (s *SiteService) Update(ctx mycontext.Context, id int64, siteReq *entity.Site) (*entity.Site, error) {
existingSite, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
existingSite.ToUpdatedSite(ctx.RequestedBy(), *siteReq)
updatedSiteDB, err := s.repo.Update(ctx, existingSite.ToSiteDB())
if err != nil {
logger.ContextLogger(ctx).Error("error when updating site", zap.Error(err))
return nil, err
}
return updatedSiteDB.ToSite(), nil
}
func (s *SiteService) GetByID(ctx context.Context, id int64) (*entity.Site, error) {
siteDB, err := s.repo.GetByID(ctx, id)
if err != nil {
logger.ContextLogger(ctx).Error("error when getting site by ID", zap.Error(err))
return nil, err
}
return siteDB.ToSite(), nil
}
func (s *SiteService) GetAll(ctx context.Context, search entity.SiteSearch) ([]*entity.Site, int, error) {
sites, total, err := s.repo.GetAll(ctx, search)
if err != nil {
logger.ContextLogger(ctx).Error("error when getting all sites", zap.Error(err))
return nil, 0, err
}
return sites.ToSiteList(), total, nil
}
func (s *SiteService) Delete(ctx mycontext.Context, id int64) error {
siteDB, err := s.repo.GetByID(ctx, id)
if err != nil {
logger.ContextLogger(ctx).Error("error when getting site by ID", zap.Error(err))
return err
}
siteDB.SetDeleted(ctx.RequestedBy())
_, err = s.repo.Update(ctx, siteDB)
if err != nil {
logger.ContextLogger(ctx).Error("error when updating site", zap.Error(err))
return err
}
return nil
}

View File

@ -3,6 +3,7 @@ package users
import (
"fmt"
"furtuna-be/internal/common/mycontext"
"gorm.io/gorm"
"go.uber.org/zap"
@ -48,6 +49,31 @@ func (s *UserService) Create(ctx mycontext.Context, userReq *entity.User) (*enti
return userDB.ToUser(), nil
}
func (s *UserService) CreateWithTx(ctx mycontext.Context, tx *gorm.DB, userReq *entity.User) (*entity.User, error) {
//check
userExist, err := s.repo.GetUserByEmail(ctx, userReq.Email)
if err != nil {
return nil, err
}
if userExist != nil {
return nil, fmt.Errorf("Email already exist")
}
userDB, err := userReq.ToUserDB(ctx.RequestedBy())
if err != nil {
return nil, err
}
userDB, err = s.repo.CreateWithTx(ctx, tx, userDB)
if err != nil {
logger.ContextLogger(ctx).Error("error when create user", zap.Error(err))
return nil, err
}
return userDB.ToUser(), nil
}
func (s *UserService) GetAll(ctx mycontext.Context, search entity.UserSearch) ([]*entity.User, int, error) {
users, total, err := s.repo.GetAllUsers(ctx, search)
@ -82,7 +108,7 @@ func (s *UserService) Update(ctx mycontext.Context, id int64, userReq *entity.Us
return nil, err
}
existingUser.BranchName = branch.Name
existingUser.PartnerName = branch.Name
}
err = existingUser.ToUpdatedUser(*userReq)

View File

@ -3,4 +3,9 @@ CREATE TABLE roles (
role_name VARCHAR(255) NOT NULL
);
INSERT INTO roles(role_id, role_name) VALUES (1, 'super_admin'), (2, 'branch_admin');
INSERT INTO roles(role_id, role_name)
VALUES (1, 'super_admin'),
(2, 'admin'),
(3, 'admin_partner'),
(4, 'admin_site'),
(5, 'cashier');

View File

@ -2,11 +2,15 @@ CREATE TABLE partners
(
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
status varchar,
Address varchar,
status VARCHAR(50),
license_expired_date DATE,
address VARCHAR(255),
bank_name VARCHAR(255),
bank_account_number VARCHAR(50),
bank_account_holder_name VARCHAR(255),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by INTEGER,
updated_by INTEGER,
deleted_at timestamp
deleted_at TIMESTAMP
);

View File

@ -1,10 +1,13 @@
CREATE TABLE products
(
id SERIAL PRIMARY KEY,
partner_id INTEGER,
site_id INTEGER,
name VARCHAR(255) NOT NULL,
type VARCHAR,
prices INTEGER[],
price decimal,
is_weekend_ticket boolean,
is_season_ticket boolean,
status VARCHAR,
description VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),

View File

View File

@ -0,0 +1,11 @@
CREATE TABLE public.wallets
(
id SERIAL PRIMARY KEY,
partner_id INTEGER NOT NULL,
balance DECIMAL(18, 2) NOT NULL DEFAULT 0.00,
currency VARCHAR(3) NOT NULL,
status VARCHAR(50),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
FOREIGN KEY (partner_id) REFERENCES partners (id) ON DELETE CASCADE
);