Add Customer Discovery

This commit is contained in:
aditya.siregar 2024-08-03 20:01:25 +07:00
parent aeed6fde7b
commit 600d42d529
14 changed files with 620 additions and 19 deletions

View File

@ -32,6 +32,7 @@ type Config struct {
Brevo Brevo `mapstructure:"brevo"` Brevo Brevo `mapstructure:"brevo"`
Email Email `mapstructure:"email"` Email Email `mapstructure:"email"`
Withdraw Withdraw `mapstructure:"withdrawal"` Withdraw Withdraw `mapstructure:"withdrawal"`
Discovery Discovery `mapstructure:"discovery"`
} }
var ( var (

17
config/discovery.go Normal file
View File

@ -0,0 +1,17 @@
package config
type Discovery struct {
ExploreDestinations []ExploreDestination `mapstructure:"explore_destinations"`
ExploreRegions []ExploreRegion `mapstructure:"explore_regions"`
}
type ExploreDestinations []ExploreDestination
type ExploreDestination struct {
Name string `mapstructure:"name"`
ImageURL string `mapstructure:"image_url"`
}
type ExploreRegion struct {
Name string `mapstructure:"name"`
}

View File

@ -25,7 +25,7 @@ postgresql:
max-idle-connections-in-second: 600 max-idle-connections-in-second: 600
max-open-connections-in-second: 600 max-open-connections-in-second: 600
connection-max-life-time-in-second: 600 connection-max-life-time-in-second: 600
debug: true debug: false
oss: oss:
access_key_id: e50b31e5eddf63c0ZKB2 access_key_id: e50b31e5eddf63c0ZKB2
@ -54,3 +54,23 @@ email:
withdrawal: withdrawal:
platform_fee: 5000 platform_fee: 5000
discovery:
explore_destinations:
- name: "Jakarta"
image_url: "https://obs.eranyacloud.com/furtuna-dev/file/03c0b046-43ab-4d35-a743-6a173bc66b90-1722680749.png"
- name: "Banten"
image_url: "https://obs.eranyacloud.com/furtuna-dev/file/c8e7dd8a-17be-449f-afdc-0c07eda438ce-1722680809.png"
- name: "Yogyakarta"
image_url: "https://obs.eranyacloud.com/furtuna-dev/file/83b78c19-4c97-48c9-bc97-a7403e1c4eed-1722680828.png"
- name: "Jawa Barat"
image_url: "https://obs.eranyacloud.com/furtuna-dev/file/07c35ab1-3e20-4858-8d7d-b29517239dc3-1722680848.png"
- name: "Jawa Tengah"
image_url: "https://obs.eranyacloud.com/furtuna-dev/file/a1915a98-c2aa-4997-8e75-bd4e43789b0c-1722680874.png"
- name: "Jawa Timur"
image_url: "https://obs.eranyacloud.com/furtuna-dev/file/7b5d2b86-e8a8-4703-a153-c186021cf088-1722680894.png"
explore_regions:
- name: "Jawa"
- name: "Sumatera"
- name: "Kalimantan"
- name: "Sulawesi"

View File

@ -0,0 +1,39 @@
package entity
type DiscoverySearch struct {
Lat float64
Long float64
Name string
Region string
Discover string
Offset int
Limit int
Radius int
}
type DiscoverySearchResp struct {
ExploreRegions []ExploreRegion `json:"exploreRegions"`
ExploreDestinations []ExploreDestination `json:"exploreDestinations"`
MustVisit []MustVisit `json:"mustVisit"`
}
type ExploreRegion struct {
Name string `json:"name"`
}
type ExploreDestination struct {
Name string `json:"name"`
ImageURL string `json:"image_url"`
}
type MustVisit struct {
SiteID int64 `json:"site_id"`
Name string `json:"name"`
Location string `json:"location"`
Rating float64 `json:"rating"`
ReviewCount int `json:"reviewCount"`
Price float64 `json:"price"`
ImageURL string `json:"imageUrl"`
Region string `json:"region"`
Regency string `json:"regency"`
}

View File

@ -25,6 +25,10 @@ type Site struct {
CreatedBy int64 `gorm:"type:int;column:created_by"` CreatedBy int64 `gorm:"type:int;column:created_by"`
UpdatedBy int64 `gorm:"type:int;column:updated_by"` UpdatedBy int64 `gorm:"type:int;column:updated_by"`
Products []Product `gorm:"foreignKey:SiteID;constraint:OnDelete:CASCADE;"` Products []Product `gorm:"foreignKey:SiteID;constraint:OnDelete:CASCADE;"`
Latitude *float64 `json:"latitude"`
Longitude *float64 `json:"longitude"`
Region string `json:"region"`
Distance float64 `json:"distance"`
} }
type SiteSearch struct { type SiteSearch struct {
@ -166,3 +170,34 @@ func (e *SiteCountDB) ToSiteCount() *SiteCount {
Count: e.Count, Count: e.Count,
} }
} }
type SiteProductInfo struct {
SiteID int64 `json:"site_id"`
SiteName string `json:"site_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 time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
Distance float64 `json:"distance"` // Calculated field
ProductID int64 `json:"product_id"`
ProductName string `json:"product_name"`
ProductType string `json:"product_type"`
ProductPrice float64 `json:"product_price"`
IsWeekendTicket bool `json:"is_weekend_ticket"`
ProductStatus string `json:"product_status"`
ProductDescription string `json:"product_description"`
Region string `json:"region"`
Regency string `json:"regency"`
}

View File

@ -0,0 +1,134 @@
package discovery
import (
"furtuna-be/internal/entity"
"github.com/gin-gonic/gin"
"net/http"
"furtuna-be/internal/common/errors"
"furtuna-be/internal/handlers/request"
"furtuna-be/internal/handlers/response"
"furtuna-be/internal/services"
)
type Handler struct {
service services.DiscoverService
}
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/discovery")
route.GET("/home", h.DisoveryHome)
route.GET("/search", h.DisoverySearch)
}
func NewHandler(service services.DiscoverService) *Handler {
return &Handler{
service: service,
}
}
func (h *Handler) DisoveryHome(c *gin.Context) {
var req request.DiscoveryHomeParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, err := h.service.Home(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: ConvertEntityToResponse(res),
})
}
func (h *Handler) DisoverySearch(c *gin.Context) {
var req request.DiscoveryHomeParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, total, err := h.service.Search(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: ConvertEntityToSearchResponse(res, total, req),
})
}
func ConvertEntityToResponse(entityResp *entity.DiscoverySearchResp) *response.ExploreResponse {
// Convert ExploreRegions
exploreRegions := make([]response.Region, len(entityResp.ExploreRegions))
for i, region := range entityResp.ExploreRegions {
exploreRegions[i] = response.Region{
Name: region.Name,
}
}
// Convert ExploreDestinations
exploreDestinations := make([]response.Destination, len(entityResp.ExploreDestinations))
for i, destination := range entityResp.ExploreDestinations {
exploreDestinations[i] = response.Destination{
Name: destination.Name,
ImageURL: destination.ImageURL,
}
}
mustVisit := make([]response.MustVisit, len(entityResp.MustVisit))
for i, mv := range entityResp.MustVisit {
mustVisit[i] = response.MustVisit{
Name: mv.Name,
Region: mv.Region,
Rating: mv.Rating,
ReviewCount: mv.ReviewCount,
Price: mv.Price,
ImageURL: mv.ImageURL,
SiteID: mv.SiteID,
Regency: mv.Regency,
}
}
return &response.ExploreResponse{
ExploreRegions: exploreRegions,
ExploreDestinations: exploreDestinations,
MustVisit: mustVisit,
}
}
func ConvertEntityToSearchResponse(entityResp *entity.DiscoverySearchResp, total int64, req request.DiscoveryHomeParam) *response.SearchResponse {
data := make([]response.SiteSeach, len(entityResp.MustVisit))
for i, mv := range entityResp.MustVisit {
data[i] = response.SiteSeach{
Name: mv.Name,
Region: mv.Region,
Rating: mv.Rating,
ReviewCount: mv.ReviewCount,
Price: mv.Price,
ImageURL: mv.ImageURL,
SiteID: mv.SiteID,
Regency: mv.Regency,
}
}
return &response.SearchResponse{
Data: data,
Total: int(total),
Limit: req.Limit,
Offset: req.Offset,
}
}

View File

@ -0,0 +1,33 @@
package request
import (
"furtuna-be/internal/entity"
)
type DiscoveryHomeParam struct {
Lat float64 `form:"lat" json:"lat" example:"10"`
Long float64 `form:"long" json:"long" example:"0"`
Name string `form:"name" json:"name" example:"0"`
Region string `form:"region" json:"region" example:"0"`
Radius int `form:"radius" json:"radius" example:"0"`
Limit int `form:"limit" json:"limit" example:"0"`
Offset int `form:"offset" json:"offset" example:"0"`
Discover string `form:"discover" json:"discover" example:"0"`
}
func (d *DiscoveryHomeParam) ToEntity() *entity.DiscoverySearch {
if d.Limit == 0 {
d.Limit = 10
}
return &entity.DiscoverySearch{
Lat: d.Lat,
Long: d.Long,
Name: d.Name,
Region: d.Region,
Radius: d.Radius,
Limit: d.Limit,
Offset: d.Offset,
Discover: d.Discover,
}
}

View File

@ -0,0 +1,49 @@
package response
type ExploreResponse struct {
ExploreRegions []Region `json:"exploreRegions"`
ExploreDestinations []Destination `json:"exploreDestinations"`
MustVisit []MustVisit `json:"mustVisit"`
}
type CurrentLocation struct {
City string `json:"city"`
}
type Region struct {
Name string `json:"name"`
}
type Destination struct {
Name string `json:"name"`
ImageURL string `json:"image_url"`
}
type MustVisit struct {
SiteID int64 `json:"site_id"`
Name string `json:"name"`
Region string `json:"region"`
Rating float64 `json:"rating"`
ReviewCount int `json:"reviewCount"`
Price float64 `json:"price"`
ImageURL string `json:"imageUrl"`
Regency string `json:"regency"`
}
type SearchResponse struct {
Offset int `json:"offset"`
Total int `json:"total"`
Limit int `json:"limit"`
Data []SiteSeach `json:"data"`
}
type SiteSeach struct {
SiteID int64 `json:"site_id"`
Name string `json:"name"`
Region string `json:"region"`
Rating float64 `json:"rating"`
ReviewCount int `json:"reviewCount"`
Price float64 `json:"price"`
ImageURL string `json:"imageUrl"`
Regency string `json:"regency"`
}

View File

@ -172,6 +172,8 @@ type SiteRepository interface {
GetAll(ctx context.Context, req entity.SiteSearch) (entity.SiteList, int, error) GetAll(ctx context.Context, req entity.SiteSearch) (entity.SiteList, int, error)
Delete(ctx context.Context, id int64) error Delete(ctx context.Context, id int64) error
Count(ctx mycontext.Context, req entity.SiteSearch) (*entity.SiteCountDB, error) Count(ctx mycontext.Context, req entity.SiteSearch) (*entity.SiteCountDB, error)
GetNearestSites(ctx context.Context, latitude, longitude, radius float64) ([]entity.SiteProductInfo, error)
SearchSites(ctx context.Context, search *entity.DiscoverySearch) ([]entity.SiteProductInfo, int64, error)
} }
type TransactionManager interface { type TransactionManager interface {

View File

@ -156,3 +156,119 @@ func (r *SiteRepository) Count(ctx mycontext.Context, req entity.SiteSearch) (*e
return count, nil return count, nil
} }
func (r *SiteRepository) GetNearestSites(ctx context.Context, latitude, longitude, radius float64) ([]entity.SiteProductInfo, error) {
const limit = 5
var siteProducts []entity.SiteProductInfo
distanceQuery := `
(6371 * acos(cos(radians(?)) * cos(radians(latitude)) * cos(radians(longitude) - radians(?)) + sin(radians(?)) * sin(radians(latitude))))
`
// Primary query for sites within the radius
err := r.db.WithContext(ctx).Raw(`
SELECT s.id AS site_id, s.name AS site_name, s.region, s.regency, s.partner_id, s.image, s.address, s.location_link, s.description,
s.highlight, s.contact_person, s.tnc, s.additional_info, s.status, s.is_season_ticket, s.is_discount_active,
s.latitude, s.longitude, s.created_at, s.updated_at,
`+distanceQuery+` AS distance,
p.id AS product_id, p.name AS product_name, p.type AS product_type, p.price AS product_price,
p.is_weekend_ticket, p.is_season_ticket, p.status AS product_status, p.description AS product_description
FROM sites s
LEFT JOIN (
SELECT *, ROW_NUMBER() OVER (PARTITION BY site_id ORDER BY price ASC) AS rn
FROM products
) p ON s.id = p.site_id AND p.rn = 1
WHERE `+distanceQuery+` < ?
ORDER BY distance
LIMIT ?`,
latitude, longitude, latitude, latitude, longitude, latitude, radius, limit).Scan(&siteProducts).Error
if err != nil {
return nil, err
}
// If fewer than 5 sites found, fetch additional ones regardless of distance
if len(siteProducts) < limit {
additionalLimit := limit - len(siteProducts)
err = r.db.WithContext(ctx).Raw(`
SELECT s.id AS site_id, s.name AS site_name, s.region, s.regency, s.partner_id, s.image, s.address, s.location_link, s.description,
s.highlight, s.contact_person, s.tnc, s.additional_info, s.status, s.is_season_ticket, s.is_discount_active,
s.latitude, s.longitude, s.created_at, s.updated_at,
`+distanceQuery+` AS distance,
p.id AS product_id, p.name AS product_name, p.type AS product_type, p.price AS product_price,
p.is_weekend_ticket, p.is_season_ticket, p.status AS product_status, p.description AS product_description
FROM sites s
LEFT JOIN (
SELECT *, ROW_NUMBER() OVER (PARTITION BY site_id ORDER BY price ASC) AS rn
FROM products
) p ON s.id = p.site_id AND p.rn = 1
ORDER BY distance
LIMIT ?`,
latitude, longitude, latitude, additionalLimit).Scan(&siteProducts).Error
if err != nil {
return nil, err
}
}
return siteProducts, nil
}
func (r *SiteRepository) SearchSites(ctx context.Context, search *entity.DiscoverySearch) ([]entity.SiteProductInfo, int64, error) {
var siteProducts []entity.SiteProductInfo
var total int64
// Adding wildcard for partial matching
searchName := "%" + search.Name + "%"
// Base conditions and parameters
conditions := "s.name ILIKE ?"
params := []interface{}{searchName}
// Add region filtering if region is provided
if search.Region != "" {
conditions += " AND s.region = ?"
params = append(params, search.Region)
}
if search.Discover != "" {
conditions += " AND s.address ILIKE ?"
params = append(params, "%"+search.Discover+"%")
}
// Count query to get the total number of matching records
countQuery := `
SELECT COUNT(*)
FROM sites s
WHERE ` + conditions
err := r.db.WithContext(ctx).Raw(countQuery, params...).Scan(&total).Error
if err != nil {
return nil, 0, err
}
// Add limit and offset for the data query
dataParams := append(params, search.Limit, search.Offset)
// Primary query for sites matching name and region, with pagination
dataQuery := `
SELECT s.id AS site_id, s.name AS site_name, s.region, s.regency, s.partner_id, s.image, s.address, s.location_link, s.description,
s.highlight, s.contact_person, s.tnc, s.additional_info, s.status, s.is_season_ticket, s.is_discount_active,
s.latitude, s.longitude, s.created_at, s.updated_at,
p.id AS product_id, p.name AS product_name, p.type AS product_type, p.price AS product_price,
p.is_weekend_ticket, p.is_season_ticket, p.status AS product_status, p.description AS product_description
FROM sites s
LEFT JOIN (
SELECT *, ROW_NUMBER() OVER (PARTITION BY site_id ORDER BY price ASC) AS rn
FROM products
) p ON s.id = p.site_id AND p.rn = 1
WHERE ` + conditions + `
ORDER BY s.name
LIMIT ? OFFSET ?
`
err = r.db.WithContext(ctx).Raw(dataQuery, dataParams...).Scan(&siteProducts).Error
if err != nil {
return nil, 0, err
}
return siteProducts, total, nil
}

View File

@ -0,0 +1,25 @@
package routes
import (
"furtuna-be/internal/handlers/http/discovery"
"furtuna-be/internal/middlewares"
"furtuna-be/internal/app"
"furtuna-be/internal/repository"
"furtuna-be/internal/services"
)
func RegisterCustomerRoutes(app *app.Server, serviceManager *services.ServiceManagerImpl,
repoManager *repository.RepoManagerImpl) {
approute := app.Group("/api/v1/customer")
authMiddleware := middlewares.AuthorizationMiddleware(repoManager.Crypto)
serverRoutes := []HTTPHandlerRoutes{
discovery.NewHandler(serviceManager.DiscoverService),
}
for _, handler := range serverRoutes {
handler.Route(approute, authMiddleware)
}
}

View File

@ -0,0 +1,121 @@
package discovery
import (
"context"
"furtuna-be/config"
"furtuna-be/internal/entity"
"furtuna-be/internal/repository"
)
const (
defaultLatitude = -6.2088
defaultLongitude = 106.8456
radius = 10000
)
type DiscoveryService struct {
repo repository.SiteRepository
cfg config.Discovery
}
func NewDiscoveryService(repo repository.SiteRepository, cfg config.Discovery) *DiscoveryService {
return &DiscoveryService{
repo: repo,
cfg: cfg,
}
}
func (s *DiscoveryService) Home(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, error) {
if search.Lat == 0 || search.Long == 0 {
search.Lat = defaultLatitude
search.Long = defaultLongitude
}
siteProducts, err := s.repo.GetNearestSites(ctx, search.Lat, search.Long, radius)
if err != nil {
return nil, err
}
exploreDestinations := []entity.ExploreDestination{}
for _, exploreDestination := range s.cfg.ExploreDestinations {
exploreDestinations = append(exploreDestinations, entity.ExploreDestination{
Name: exploreDestination.Name,
ImageURL: exploreDestination.ImageURL,
})
}
exploreRegions := []entity.ExploreRegion{}
for _, exploreRegion := range s.cfg.ExploreRegions {
exploreRegions = append(exploreRegions, entity.ExploreRegion{
Name: exploreRegion.Name,
})
}
mustVisits := []entity.MustVisit{}
for _, siteProduct := range siteProducts {
mustVisits = append(mustVisits, entity.MustVisit{
Name: siteProduct.SiteName,
Price: siteProduct.ProductPrice,
Region: siteProduct.Region,
SiteID: siteProduct.SiteID,
ImageURL: siteProduct.Image,
})
}
response := &entity.DiscoverySearchResp{
ExploreRegions: exploreRegions,
ExploreDestinations: exploreDestinations,
MustVisit: mustVisits,
}
return response, nil
}
func (s *DiscoveryService) Search(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, int64, error) {
if search.Lat == 0 || search.Long == 0 {
search.Lat = defaultLatitude
search.Long = defaultLongitude
search.Radius = radius
}
siteProducts, total, err := s.repo.SearchSites(ctx, search)
if err != nil {
return nil, 0, err
}
exploreDestinations := []entity.ExploreDestination{}
for _, exploreDestination := range s.cfg.ExploreDestinations {
exploreDestinations = append(exploreDestinations, entity.ExploreDestination{
Name: exploreDestination.Name,
ImageURL: exploreDestination.ImageURL,
})
}
exploreRegions := []entity.ExploreRegion{}
for _, exploreRegion := range s.cfg.ExploreRegions {
exploreRegions = append(exploreRegions, entity.ExploreRegion{
Name: exploreRegion.Name,
})
}
mustVisits := []entity.MustVisit{}
for _, siteProduct := range siteProducts {
mustVisits = append(mustVisits, entity.MustVisit{
Name: siteProduct.SiteName,
Price: siteProduct.ProductPrice,
Region: siteProduct.Region,
SiteID: siteProduct.SiteID,
ImageURL: siteProduct.Image,
Regency: siteProduct.Regency,
})
}
response := &entity.DiscoverySearchResp{
ExploreRegions: exploreRegions,
ExploreDestinations: exploreDestinations,
MustVisit: mustVisits,
}
return response, total, nil
}

View File

@ -5,6 +5,7 @@ import (
"furtuna-be/internal/common/mycontext" "furtuna-be/internal/common/mycontext"
"furtuna-be/internal/services/balance" "furtuna-be/internal/services/balance"
"furtuna-be/internal/services/branch" "furtuna-be/internal/services/branch"
"furtuna-be/internal/services/discovery"
service "furtuna-be/internal/services/license" service "furtuna-be/internal/services/license"
"furtuna-be/internal/services/order" "furtuna-be/internal/services/order"
"furtuna-be/internal/services/oss" "furtuna-be/internal/services/oss"
@ -25,19 +26,20 @@ import (
) )
type ServiceManagerImpl struct { type ServiceManagerImpl struct {
AuthSvc Auth AuthSvc Auth
EventSvc Event EventSvc Event
UserSvc User UserSvc User
BranchSvc Branch BranchSvc Branch
StudioSvc Studio StudioSvc Studio
ProductSvc Product ProductSvc Product
OrderSvc Order OrderSvc Order
OSSSvc OSSService OSSSvc OSSService
PartnerSvc Partner PartnerSvc Partner
SiteSvc Site SiteSvc Site
LicenseSvc License LicenseSvc License
Transaction Transaction Transaction Transaction
Balance Balance Balance Balance
DiscoverService DiscoverService
} }
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl { func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
@ -52,10 +54,11 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
OSSSvc: oss.NewOSSService(repo.OSS), OSSSvc: oss.NewOSSService(repo.OSS),
PartnerSvc: partner.NewPartnerService( PartnerSvc: partner.NewPartnerService(
repo.Partner, users.NewUserService(repo.User, repo.Branch), repo.Trx, repo.Wallet), 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), LicenseSvc: service.NewLicenseService(repo.License),
Transaction: transaction.New(repo.Transaction, repo.Wallet, repo.Trx), Transaction: transaction.New(repo.Transaction, repo.Wallet, repo.Trx),
Balance: balance.NewBalanceService(repo.Wallet, repo.Trx, repo.Crypto, &cfg.Withdraw, repo.Transaction), Balance: balance.NewBalanceService(repo.Wallet, repo.Trx, repo.Crypto, &cfg.Withdraw, repo.Transaction),
DiscoverService: discovery.NewDiscoveryService(repo.Site, cfg.Discovery),
} }
} }
@ -156,3 +159,8 @@ type Balance interface {
WithdrawInquiry(ctx context.Context, req *entity.BalanceWithdrawInquiry) (*entity.BalanceWithdrawInquiryResponse, error) WithdrawInquiry(ctx context.Context, req *entity.BalanceWithdrawInquiry) (*entity.BalanceWithdrawInquiryResponse, error)
WithdrawExecute(ctx mycontext.Context, req *entity.WalletWithdrawRequest) (*entity.WalletWithdrawResponse, error) WithdrawExecute(ctx mycontext.Context, req *entity.WalletWithdrawRequest) (*entity.WalletWithdrawResponse, error)
} }
type DiscoverService interface {
Home(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, error)
Search(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, int64, error)
}

View File

@ -32,6 +32,7 @@ func main() {
routes.RegisterPublicRoutes(server, service, repo) routes.RegisterPublicRoutes(server, service, repo)
routes.RegisterPrivateRoutes(server, service, repo) routes.RegisterPrivateRoutes(server, service, repo)
routes.RegisterCustomerRoutes(server, service, repo)
server.StartScheduler() server.StartScheduler()