update
This commit is contained in:
parent
09c9a4d59d
commit
06d7b4764f
@ -82,5 +82,9 @@ func (c *Config) Auth() *AuthConfig {
|
|||||||
secret: c.Jwt.TokenWithdraw.Secret,
|
secret: c.Jwt.TokenWithdraw.Secret,
|
||||||
expireTTL: c.Jwt.TokenWithdraw.ExpiresTTL,
|
expireTTL: c.Jwt.TokenWithdraw.ExpiresTTL,
|
||||||
},
|
},
|
||||||
|
jwtCustomer: JWT{
|
||||||
|
secret: c.Jwt.TokenCustomer.Secret,
|
||||||
|
expireTTL: c.Jwt.TokenCustomer.ExpiresTTL,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ type AuthConfig struct {
|
|||||||
jwtOrderExpiresTTL int
|
jwtOrderExpiresTTL int
|
||||||
jwtSecretResetPassword JWT
|
jwtSecretResetPassword JWT
|
||||||
jwtWithdraw JWT
|
jwtWithdraw JWT
|
||||||
|
jwtCustomer JWT
|
||||||
}
|
}
|
||||||
|
|
||||||
type JWT struct {
|
type JWT struct {
|
||||||
@ -24,6 +25,10 @@ func (c *AuthConfig) AccessTokenOrderSecret() string {
|
|||||||
return c.jwtOrderSecret
|
return c.jwtOrderSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *AuthConfig) AccessTokenCustomerSecret() string {
|
||||||
|
return c.jwtCustomer.secret
|
||||||
|
}
|
||||||
|
|
||||||
func (c *AuthConfig) AccessTokenWithdrawSecret() string {
|
func (c *AuthConfig) AccessTokenWithdrawSecret() string {
|
||||||
return c.jwtWithdraw.secret
|
return c.jwtWithdraw.secret
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ type Jwt struct {
|
|||||||
TokenOrder Token `mapstructure:"token-order"`
|
TokenOrder Token `mapstructure:"token-order"`
|
||||||
TokenResetPassword Token `mapstructure:"token-reset-password"`
|
TokenResetPassword Token `mapstructure:"token-reset-password"`
|
||||||
TokenWithdraw Token `mapstructure:"token-withdraw"`
|
TokenWithdraw Token `mapstructure:"token-withdraw"`
|
||||||
|
TokenCustomer Token `mapstructure:"token-customer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
|
|||||||
@ -13,6 +13,9 @@ jwt:
|
|||||||
token-withdraw:
|
token-withdraw:
|
||||||
expires-ttl: 2
|
expires-ttl: 2
|
||||||
secret: "909Lm25V3Qd7aut8dr4QUxm5PZUrSFs"
|
secret: "909Lm25V3Qd7aut8dr4QUxm5PZUrSFs"
|
||||||
|
token-customer:
|
||||||
|
expires-ttl: 1400
|
||||||
|
secret: "WakLm25V3Qd7aut8dr4QUxm5PZUrWa#"
|
||||||
|
|
||||||
postgresql:
|
postgresql:
|
||||||
host: 62.72.45.250
|
host: 62.72.45.250
|
||||||
|
|||||||
@ -81,6 +81,14 @@ func NewMyContext(parent context.Context, claims *entity.JWTAuthClaims) (*MyCont
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMyContextCustomer(parent context.Context, claims *entity.JWTAuthClaimsCustomer) (*MyContextImpl, error) {
|
||||||
|
return &MyContextImpl{
|
||||||
|
Context: parent,
|
||||||
|
requestedBy: claims.UserID,
|
||||||
|
name: claims.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewContext(parent context.Context) *MyContextImpl {
|
func NewContext(parent context.Context) *MyContextImpl {
|
||||||
return &MyContextImpl{
|
return &MyContextImpl{
|
||||||
Context: parent,
|
Context: parent,
|
||||||
|
|||||||
@ -13,6 +13,13 @@ type JWTAuthClaims struct {
|
|||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JWTAuthClaimsCustomer struct {
|
||||||
|
UserID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
jwt.StandardClaims
|
||||||
|
}
|
||||||
|
|
||||||
type JWTOrderClaims struct {
|
type JWTOrderClaims struct {
|
||||||
PartnerID int64 `json:"id"`
|
PartnerID int64 `json:"id"`
|
||||||
OrderID int64 `json:"order_id"`
|
OrderID int64 `json:"order_id"`
|
||||||
|
|||||||
@ -75,18 +75,19 @@ func (Order) TableName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OrderItem struct {
|
type OrderItem struct {
|
||||||
ID int64 `gorm:"primaryKey;autoIncrement;column:order_item_id"`
|
ID int64 `gorm:"primaryKey;autoIncrement;column:order_item_id"`
|
||||||
OrderID int64 `gorm:"type:int;column:order_id"`
|
OrderID int64 `gorm:"type:int;column:order_id"`
|
||||||
ItemID int64 `gorm:"type:int;column:item_id"`
|
ItemID int64 `gorm:"type:int;column:item_id"`
|
||||||
ItemType string `gorm:"type:varchar;column:item_type"`
|
ItemType string `gorm:"type:varchar;column:item_type"`
|
||||||
Price float64 `gorm:"type:numeric;not null;column:price"`
|
Price float64 `gorm:"type:numeric;not null;column:price"`
|
||||||
Quantity int `gorm:"type:int;column:quantity"`
|
Quantity int `gorm:"type:int;column:quantity"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
|
CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
|
||||||
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"`
|
||||||
Product *Product `gorm:"foreignKey:ItemID;references:ID"`
|
Product *Product `gorm:"foreignKey:ItemID;references:ID"`
|
||||||
ItemName string `gorm:"type:varchar;column:item_name"`
|
ItemName string `gorm:"type:varchar;column:item_name"`
|
||||||
|
Description string `gorm:"type:varchar;column:description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (OrderItem) TableName() string {
|
func (OrderItem) TableName() string {
|
||||||
@ -110,8 +111,9 @@ type OrderRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OrderItemRequest struct {
|
type OrderItemRequest struct {
|
||||||
ProductID int64 `json:"product_id" validate:"required"`
|
ProductID int64 `json:"product_id" validate:"required"`
|
||||||
Quantity int `json:"quantity" validate:"required"`
|
Quantity int `json:"quantity" validate:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderExecuteRequest struct {
|
type OrderExecuteRequest struct {
|
||||||
|
|||||||
@ -30,6 +30,7 @@ type ProductSearch struct {
|
|||||||
Name string
|
Name string
|
||||||
Type product.ProductType
|
Type product.ProductType
|
||||||
BranchID int64
|
BranchID int64
|
||||||
|
PartnerID int64
|
||||||
Available product.ProductStock
|
Available product.ProductStock
|
||||||
Limit int
|
Limit int
|
||||||
Offset int
|
Offset int
|
||||||
|
|||||||
@ -125,3 +125,12 @@ func (c Customer) HashedPassword() string {
|
|||||||
|
|
||||||
return string(hashedPassword)
|
return string(hashedPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Customer) ToUserAuthenticate(signedToken string) *AuthenticateUser {
|
||||||
|
return &AuthenticateUser{
|
||||||
|
ID: u.ID,
|
||||||
|
Token: signedToken,
|
||||||
|
Name: u.Name,
|
||||||
|
UserType: u.UserType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -2,8 +2,8 @@ package customerauth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
auth2 "enaklo-pos-be/internal/handlers/request"
|
auth2 "enaklo-pos-be/internal/handlers/request"
|
||||||
|
"enaklo-pos-be/internal/services/v2/auth"
|
||||||
"enaklo-pos-be/internal/services/v2/customer"
|
"enaklo-pos-be/internal/services/v2/customer"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AuthHandler struct {
|
type AuthHandler struct {
|
||||||
service services.Auth
|
service auth.Service
|
||||||
userService services.User
|
userService services.User
|
||||||
customerSvc customer.Service
|
customerSvc customer.Service
|
||||||
}
|
}
|
||||||
@ -23,49 +23,29 @@ type AuthHandler struct {
|
|||||||
func (a *AuthHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
func (a *AuthHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
||||||
authRoute := group.Group("/auth")
|
authRoute := group.Group("/auth")
|
||||||
authRoute.POST("/login", a.AuthLogin)
|
authRoute.POST("/login", a.AuthLogin)
|
||||||
authRoute.POST("/forgot-password", a.ForgotPassword)
|
|
||||||
authRoute.POST("/reset-password", jwt, a.ResetPassword)
|
|
||||||
authRoute.POST("/register", a.Register)
|
|
||||||
authRoute.POST("/verify", a.VerifyRegistration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthHandler(service services.Auth, userService services.User, customerSvc customer.Service) *AuthHandler {
|
func NewAuthHandler(service auth.Service) *AuthHandler {
|
||||||
return &AuthHandler{
|
return &AuthHandler{
|
||||||
service: service,
|
service: service,
|
||||||
userService: userService,
|
|
||||||
customerSvc: customerSvc,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthLogin handles the authentication process for user login.
|
|
||||||
// @Summary User login
|
|
||||||
// @Description Authenticates a user based on the provided credentials and returns a JWT token.
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param bodyParam body auth2.LoginRequest true "User login credentials"
|
|
||||||
// @Success 200 {object} response.BaseResponse{data=response.LoginResponse} "Login successful"
|
|
||||||
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
|
|
||||||
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
|
|
||||||
// @Router /api/v1/auth/login [post]
|
|
||||||
// @Tags Auth Login API's
|
|
||||||
func (h *AuthHandler) AuthLogin(c *gin.Context) {
|
func (h *AuthHandler) AuthLogin(c *gin.Context) {
|
||||||
|
ctx := auth2.GetMyContext(c)
|
||||||
|
|
||||||
var bodyParam auth2.LoginRequest
|
var bodyParam auth2.LoginRequest
|
||||||
if err := c.ShouldBindJSON(&bodyParam); err != nil {
|
if err := c.ShouldBindJSON(&bodyParam); err != nil {
|
||||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
email := strings.ToLower(bodyParam.Email)
|
email := strings.ToLower(bodyParam.Email)
|
||||||
authUser, err := h.service.AuthenticateUser(c, email, bodyParam.Password)
|
authUser, err := h.service.AuthCustomer(ctx, email, bodyParam.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ErrorWrapper(c, err)
|
response.ErrorWrapper(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if authUser.UserType != "CUSTOMER" {
|
|
||||||
response.ErrorWrapper(c, errors.ErrorUserIsNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := response.LoginResponseCustoemr{
|
resp := response.LoginResponseCustoemr{
|
||||||
ID: authUser.ID,
|
ID: authUser.ID,
|
||||||
Token: authUser.Token,
|
Token: authUser.Token,
|
||||||
@ -80,118 +60,3 @@ func (h *AuthHandler) AuthLogin(c *gin.Context) {
|
|||||||
Data: resp,
|
Data: resp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForgotPassword handles the request for password reset.
|
|
||||||
// @Summary Request password reset
|
|
||||||
// @Description Sends a password reset link to the user's email.
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param bodyParam body auth2.ForgotPasswordRequest true "User email"
|
|
||||||
// @Success 200 {object} response.BaseResponse "Password reset link sent"
|
|
||||||
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
|
|
||||||
// @Router /api/v1/auth/forgot-password [post]
|
|
||||||
// @Tags Auth Password API's
|
|
||||||
func (h *AuthHandler) ForgotPassword(c *gin.Context) {
|
|
||||||
var bodyParam auth2.ResetPasswordRequest
|
|
||||||
if err := c.ShouldBindJSON(&bodyParam); err != nil {
|
|
||||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := h.service.SendPasswordResetLink(c, bodyParam.Email)
|
|
||||||
if err != nil {
|
|
||||||
response.ErrorWrapper(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response.BaseResponse{
|
|
||||||
Success: true,
|
|
||||||
Status: http.StatusOK,
|
|
||||||
Message: "Password reset link sent",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetPassword handles the password reset process.
|
|
||||||
// @Summary Reset user password
|
|
||||||
// @Description Resets the user's password using the provided token.
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param bodyParam body auth2.ResetPasswordRequest true "Reset password details"
|
|
||||||
// @Success 200 {object} response.BaseResponse "Password reset successful"
|
|
||||||
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
|
|
||||||
// @Router /api/v1/auth/reset-password [post]
|
|
||||||
// @Tags Auth Password API's
|
|
||||||
func (h *AuthHandler) ResetPassword(c *gin.Context) {
|
|
||||||
ctx := auth2.GetMyContext(c)
|
|
||||||
|
|
||||||
var req auth2.ResetPasswordChangeRequest
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := req.Validate(); err != nil {
|
|
||||||
response.ErrorWrapper(c, errors.NewError(
|
|
||||||
errors.ErrorBadRequest.ErrorType(),
|
|
||||||
fmt.Sprintf("invalid request %v", err.Error())))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := h.service.ResetPassword(ctx, req.OldPassword, req.NewPassword)
|
|
||||||
if err != nil {
|
|
||||||
response.ErrorWrapper(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response.BaseResponse{
|
|
||||||
Success: true,
|
|
||||||
Status: http.StatusOK,
|
|
||||||
Message: "Password reset successful",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *AuthHandler) Register(c *gin.Context) {
|
|
||||||
var req auth2.CustomerRegister
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := auth2.GetMyContext(c)
|
|
||||||
customer, err := h.customerSvc.RegistrationMember(ctx, req.ToEntity())
|
|
||||||
if err != nil {
|
|
||||||
response.ErrorWrapper(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response.BaseResponse{
|
|
||||||
Success: true,
|
|
||||||
Status: http.StatusOK,
|
|
||||||
Data: response.CustomerRegistrationResp{
|
|
||||||
EmailVerificationRequired: true,
|
|
||||||
PhoneVerificationRequired: false,
|
|
||||||
VerificationID: customer.VerificationID,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *AuthHandler) VerifyRegistration(c *gin.Context) {
|
|
||||||
var req auth2.VerifyEmailRequest
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := auth2.GetMyContext(c)
|
|
||||||
err := h.customerSvc.VerifyOTP(ctx, req.VerificationID, req.OTPCode)
|
|
||||||
if err != nil {
|
|
||||||
response.ErrorWrapper(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response.BaseResponse{
|
|
||||||
Success: true,
|
|
||||||
Status: http.StatusOK,
|
|
||||||
Message: "Email verification successful",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
213
internal/handlers/http/menu.go
Normal file
213
internal/handlers/http/menu.go
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"enaklo-pos-be/internal/common/errors"
|
||||||
|
"enaklo-pos-be/internal/entity"
|
||||||
|
"enaklo-pos-be/internal/handlers/request"
|
||||||
|
"enaklo-pos-be/internal/handlers/response"
|
||||||
|
"enaklo-pos-be/internal/services/v2/inprogress_order"
|
||||||
|
"enaklo-pos-be/internal/services/v2/product"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MenuHandler struct {
|
||||||
|
service product.Service
|
||||||
|
orderService inprogress_order.InProgressOrderService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMenuHandler(service product.Service, orderService inprogress_order.InProgressOrderService,
|
||||||
|
) *MenuHandler {
|
||||||
|
return &MenuHandler{
|
||||||
|
service: service,
|
||||||
|
orderService: orderService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
||||||
|
route := group.Group("/menu")
|
||||||
|
|
||||||
|
route.GET("/:partner_id", h.GetProducts)
|
||||||
|
route.POST("/order/create", h.OrderCreate)
|
||||||
|
route.POST("/order/member/create", jwt, h.OrderMemberCreate)
|
||||||
|
route.GET("/order", h.GetOrderID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) GetProducts(c *gin.Context) {
|
||||||
|
ctx := request.GetMyContext(c)
|
||||||
|
|
||||||
|
partnerIDParam := c.Param("partner_id")
|
||||||
|
partnerID, err := strconv.ParseInt(partnerIDParam, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if partnerID <= 0 {
|
||||||
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req request.ProductParam
|
||||||
|
if err := c.ShouldBindQuery(&req); err != nil {
|
||||||
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
searchParam := req.ToEntity()
|
||||||
|
searchParam.PartnerID = partnerID
|
||||||
|
|
||||||
|
products, total, err := h.service.GetProductsByPartnerID(ctx, searchParam)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.BaseResponse{
|
||||||
|
Success: true,
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: h.toProductResponseList(products, int64(total), req),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) toProductResponseList(resp []*entity.Product, total int64, req request.ProductParam) response.ProductList {
|
||||||
|
var products []response.Product
|
||||||
|
for _, b := range resp {
|
||||||
|
products = append(products, h.toProductResponse(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.ProductList{
|
||||||
|
Products: products,
|
||||||
|
Total: total,
|
||||||
|
Limit: req.Limit,
|
||||||
|
Offset: req.Offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) toProductResponse(resp *entity.Product) response.Product {
|
||||||
|
return response.Product{
|
||||||
|
ID: resp.ID,
|
||||||
|
Name: resp.Name,
|
||||||
|
Type: resp.Type,
|
||||||
|
Price: resp.Price,
|
||||||
|
Status: resp.Status,
|
||||||
|
Description: resp.Description,
|
||||||
|
Image: resp.Image,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) OrderCreate(c *gin.Context) {
|
||||||
|
ctx := request.GetMyContext(c)
|
||||||
|
|
||||||
|
var req request.OrderCustomer
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.PartnerID == 0 {
|
||||||
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orderRequest := req.ToEntity(req.PartnerID, 0)
|
||||||
|
|
||||||
|
order, err := h.orderService.Save(ctx, orderRequest)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.BaseResponse{
|
||||||
|
Success: true,
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: MapToOrderCreateResponse(order),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) OrderMemberCreate(c *gin.Context) {
|
||||||
|
ctx := request.GetMyContext(c)
|
||||||
|
|
||||||
|
var req request.OrderCustomer
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.PartnerID == 0 {
|
||||||
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := ctx.RequestedBy()
|
||||||
|
orderRequest := req.ToEntity(req.PartnerID, userID)
|
||||||
|
orderRequest.CustomerID = &userID
|
||||||
|
|
||||||
|
order, err := h.orderService.Save(ctx, orderRequest)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.BaseResponse{
|
||||||
|
Success: true,
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: MapToOrderCreateResponse(order),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetOrderParam struct {
|
||||||
|
PartnerID int64 `form:"partner_id" json:"partner_id"`
|
||||||
|
OrderID int64 `form:"order_id" json:"order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MenuHandler) GetOrderID(c *gin.Context) {
|
||||||
|
ctx := request.GetMyContext(c)
|
||||||
|
|
||||||
|
var req GetOrderParam
|
||||||
|
if err := c.ShouldBindQuery(&req); err != nil {
|
||||||
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
order, err := h.orderService.GetOrderByOrderAndPartnerID(ctx, req.PartnerID, req.OrderID)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.BaseResponse{
|
||||||
|
Success: true,
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: MapToOrderCreateResponse(order),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapToOrderCreateResponse(result *entity.Order) response.OrderResponse {
|
||||||
|
resp := response.OrderResponse{
|
||||||
|
ID: result.ID,
|
||||||
|
Status: result.Status,
|
||||||
|
Amount: result.Amount,
|
||||||
|
Tax: result.Tax,
|
||||||
|
Total: result.Total,
|
||||||
|
PaymentType: result.PaymentType,
|
||||||
|
CreatedAt: result.CreatedAt,
|
||||||
|
Items: response.MapToOrderItemResponses(result.OrderItems),
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@ type OssHandler struct {
|
|||||||
func (h *OssHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
func (h *OssHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
||||||
route := group.Group("/file")
|
route := group.Group("/file")
|
||||||
|
|
||||||
route.POST("/upload", h.UploadFile)
|
route.POST("/upload", jwt, h.UploadFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOssHandler(ossService services.OSSService) *OssHandler {
|
func NewOssHandler(ossService services.OSSService) *OssHandler {
|
||||||
|
|||||||
@ -6,12 +6,10 @@ import (
|
|||||||
"enaklo-pos-be/internal/handlers/request"
|
"enaklo-pos-be/internal/handlers/request"
|
||||||
"enaklo-pos-be/internal/handlers/response"
|
"enaklo-pos-be/internal/handlers/response"
|
||||||
"enaklo-pos-be/internal/services"
|
"enaklo-pos-be/internal/services"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
@ -275,9 +273,6 @@ func (h *Handler) toProductResponse(resp *entity.Product) response.Product {
|
|||||||
Price: resp.Price,
|
Price: resp.Price,
|
||||||
Status: resp.Status,
|
Status: resp.Status,
|
||||||
Description: resp.Description,
|
Description: resp.Description,
|
||||||
CreatedAt: resp.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: resp.CreatedAt.Format(time.RFC3339),
|
|
||||||
PartnerID: resp.PartnerID,
|
|
||||||
Image: resp.Image,
|
Image: resp.Image,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -313,14 +313,11 @@ func (h *Handler) toProductResponseList(products []entity.Product) []response.Pr
|
|||||||
for _, product := range products {
|
for _, product := range products {
|
||||||
res = append(res, response.Product{
|
res = append(res, response.Product{
|
||||||
ID: product.ID,
|
ID: product.ID,
|
||||||
PartnerID: product.PartnerID,
|
|
||||||
Name: product.Name,
|
Name: product.Name,
|
||||||
Type: product.Type,
|
Type: product.Type,
|
||||||
Price: product.Price,
|
Price: product.Price,
|
||||||
Status: product.Status,
|
Status: product.Status,
|
||||||
Description: product.Description,
|
Description: product.Description,
|
||||||
CreatedAt: product.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: product.UpdatedAt.Format(time.RFC3339),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import (
|
|||||||
func GetMyContext(c *gin.Context) mycontext.Context {
|
func GetMyContext(c *gin.Context) mycontext.Context {
|
||||||
rawCtx, exists := c.Get("myCtx")
|
rawCtx, exists := c.Get("myCtx")
|
||||||
if !exists {
|
if !exists {
|
||||||
// handle missing context
|
|
||||||
return mycontext.NewContext(c)
|
return mycontext.NewContext(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,16 @@ type Order struct {
|
|||||||
PaymentProvider string `json:"payment_provider"`
|
PaymentProvider string `json:"payment_provider"`
|
||||||
TableNumber string `json:"table_number"`
|
TableNumber string `json:"table_number"`
|
||||||
OrderItems []OrderItem `json:"order_items"`
|
OrderItems []OrderItem `json:"order_items"`
|
||||||
|
PartnerID int64 `json:"partner_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderCustomer struct {
|
||||||
|
CustomerName string `json:"customer_name" validate:"required"`
|
||||||
|
CustomerPhone string `json:"customer_phone"`
|
||||||
|
CustomerEmail string `json:"customer_email"`
|
||||||
|
TableNumber string `json:"table_number" validate:"required"`
|
||||||
|
OrderItems []OrderItem `json:"order_items" validate:"required"`
|
||||||
|
PartnerID int64 `json:"partner_id" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomerOrder struct {
|
type CustomerOrder struct {
|
||||||
@ -82,8 +92,9 @@ func (o *OrderParam) ToOrderEntity(ctx mycontext.Context) entity.OrderSearch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OrderItem struct {
|
type OrderItem struct {
|
||||||
ProductID int64 `json:"product_id" validate:"required"`
|
ProductID int64 `json:"product_id" validate:"required"`
|
||||||
Quantity int `json:"quantity" validate:"required"`
|
Quantity int `json:"quantity" validate:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Order) ToEntity(partnerID, createdBy int64) *entity.OrderRequest {
|
func (o *Order) ToEntity(partnerID, createdBy int64) *entity.OrderRequest {
|
||||||
@ -143,3 +154,23 @@ func (o *OrderParamCustomer) ToOrderEntity(ctx mycontext.Context) entity.OrderSe
|
|||||||
type OrderPrintDetail struct {
|
type OrderPrintDetail struct {
|
||||||
ID int64 `form:"id" json:"id" example:"10"`
|
ID int64 `form:"id" json:"id" example:"10"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *OrderCustomer) ToEntity(partnerID, createdBy int64) *entity.OrderRequest {
|
||||||
|
orderItems := make([]entity.OrderItemRequest, len(o.OrderItems))
|
||||||
|
for i, item := range o.OrderItems {
|
||||||
|
orderItems[i] = entity.OrderItemRequest{
|
||||||
|
ProductID: item.ProductID,
|
||||||
|
Quantity: item.Quantity,
|
||||||
|
Description: item.Description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &entity.OrderRequest{
|
||||||
|
PartnerID: partnerID,
|
||||||
|
OrderItems: orderItems,
|
||||||
|
CreatedBy: createdBy,
|
||||||
|
Source: "ONLINE_ORDER",
|
||||||
|
CustomerName: o.CustomerName,
|
||||||
|
TableNumber: o.TableNumber,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -2,14 +2,11 @@ package response
|
|||||||
|
|
||||||
type Product struct {
|
type Product struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
PartnerID int64 `json:"partner_id"`
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Price float64 `json:"price"`
|
Price float64 `json:"price"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,37 @@ func AuthorizationMiddleware(cryp repository.Crypto) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CustomerAuthorizationMiddleware(cryp repository.Crypto) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
tokenString := c.GetHeader("Authorization")
|
||||||
|
if tokenString == "" {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
|
||||||
|
|
||||||
|
claims, err := cryp.ParseAndValidateJWTCustomer(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid JWT token"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
customCtx, err := mycontext.NewMyContextCustomer(c.Request.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "error initialize context"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("myCtx", customCtx)
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SuperAdminMiddleware() gin.HandlerFunc {
|
func SuperAdminMiddleware() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
ctx, exists := c.Get("myCtx")
|
ctx, exists := c.Get("myCtx")
|
||||||
|
|||||||
@ -27,6 +27,7 @@ type CryptoConfig interface {
|
|||||||
AccessTokenResetPasswordExpire() time.Time
|
AccessTokenResetPasswordExpire() time.Time
|
||||||
AccessTokenWithdrawSecret() string
|
AccessTokenWithdrawSecret() string
|
||||||
AccessTokenWithdrawExpire() time.Time
|
AccessTokenWithdrawExpire() time.Time
|
||||||
|
AccessTokenCustomerSecret() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CryptoImpl struct {
|
type CryptoImpl struct {
|
||||||
@ -126,6 +127,21 @@ func (c *CryptoImpl) ParseAndValidateJWT(tokenString string) (*entity.JWTAuthCla
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CryptoImpl) ParseAndValidateJWTCustomer(tokenString string) (*entity.JWTAuthClaimsCustomer, error) {
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &entity.JWTAuthClaimsCustomer{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(c.Config.AccessTokenCustomerSecret()), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims, ok := token.Claims.(*entity.JWTAuthClaimsCustomer); ok && token.Valid {
|
||||||
|
return claims, nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.ErrorUnauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CryptoImpl) GenerateJWTOrder(order *entity.Order) (string, error) {
|
func (c *CryptoImpl) GenerateJWTOrder(order *entity.Order) (string, error) {
|
||||||
claims := &entity.JWTOrderClaims{
|
claims := &entity.JWTOrderClaims{
|
||||||
StandardClaims: jwt.StandardClaims{
|
StandardClaims: jwt.StandardClaims{
|
||||||
@ -282,3 +298,27 @@ func (c *CryptoImpl) ValidateJWTWithdraw(tokenString string) (*entity.WalletWith
|
|||||||
Fee: claims.Fee,
|
Fee: claims.Fee,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CryptoImpl) GenerateJWTCustomer(user *entity.Customer) (string, error) {
|
||||||
|
claims := &entity.JWTAuthClaimsCustomer{
|
||||||
|
StandardClaims: jwt.StandardClaims{
|
||||||
|
Subject: strconv.FormatInt(user.ID, 10),
|
||||||
|
ExpiresAt: c.Config.AccessTokenExpiresDate().Unix(),
|
||||||
|
IssuedAt: time.Now().Unix(),
|
||||||
|
NotBefore: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
UserID: user.ID,
|
||||||
|
Name: user.Name,
|
||||||
|
Email: user.Email,
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := jwt.
|
||||||
|
NewWithClaims(jwt.SigningMethodHS256, claims).
|
||||||
|
SignedString([]byte(c.Config.AccessTokenCustomerSecret()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -157,7 +157,7 @@ func (r *customerRepository) AddPoints(ctx mycontext.Context, customerID int64,
|
|||||||
Reference: reference,
|
Reference: reference,
|
||||||
PointsEarned: points,
|
PointsEarned: points,
|
||||||
TransactionDate: time.Now(),
|
TransactionDate: time.Now(),
|
||||||
Status: "SUCCESS",
|
Status: "active",
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Create(&pointTransaction).Error; err != nil {
|
if err := tx.Create(&pointTransaction).Error; err != nil {
|
||||||
@ -255,6 +255,7 @@ func (r *customerRepository) toDomainCustomerModel(dbModel *models.CustomerDB) *
|
|||||||
UpdatedAt: dbModel.UpdatedAt,
|
UpdatedAt: dbModel.UpdatedAt,
|
||||||
CustomerID: dbModel.CustomerID,
|
CustomerID: dbModel.CustomerID,
|
||||||
BirthDate: dbModel.BirthDate,
|
BirthDate: dbModel.BirthDate,
|
||||||
|
Password: dbModel.Password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,7 +50,7 @@ func (CustomerPointsDB) TableName() string {
|
|||||||
type CustomerPointTransactionDB struct {
|
type CustomerPointTransactionDB struct {
|
||||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||||
CustomerID int64 `gorm:"column:customer_id;not null"`
|
CustomerID int64 `gorm:"column:customer_id;not null"`
|
||||||
Reference string `gorm:"column:transaction_id"`
|
Reference string `gorm:"column:reference"`
|
||||||
PointsEarned int `gorm:"column:points_earned;not null"`
|
PointsEarned int `gorm:"column:points_earned;not null"`
|
||||||
TransactionDate time.Time `gorm:"column:transaction_date;not null"`
|
TransactionDate time.Time `gorm:"column:transaction_date;not null"`
|
||||||
ExpirationDate *time.Time `gorm:"column:expiration_date"`
|
ExpirationDate *time.Time `gorm:"column:expiration_date"`
|
||||||
|
|||||||
@ -37,6 +37,7 @@ type OrderRepository interface {
|
|||||||
ctx mycontext.Context,
|
ctx mycontext.Context,
|
||||||
req entity.PopularProductsRequest,
|
req entity.PopularProductsRequest,
|
||||||
) ([]entity.PopularProductItem, error)
|
) ([]entity.PopularProductItem, error)
|
||||||
|
FindByIDAndPartnerID(ctx mycontext.Context, id int64, partnerID int64) (*entity.Order, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type orderRepository struct {
|
type orderRepository struct {
|
||||||
@ -60,16 +61,32 @@ func (r *orderRepository) Create(ctx mycontext.Context, order *entity.Order) (*e
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := tx.Create(&orderDB).Error; err != nil {
|
if order.InProgressOrderID != 0 {
|
||||||
tx.Rollback()
|
orderDB.ID = order.InProgressOrderID
|
||||||
return nil, errors.Wrap(err, "failed to insert order")
|
|
||||||
}
|
|
||||||
|
|
||||||
order.ID = orderDB.ID
|
if err := tx.Omit("customer_id", "partner_id", "customer_name", "created_by").Save(&orderDB).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to update in-progress order")
|
||||||
|
}
|
||||||
|
|
||||||
|
order.ID = order.InProgressOrderID
|
||||||
|
|
||||||
|
if err := tx.Where("order_id = ?", order.ID).Delete(&models.OrderItemDB{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to delete existing order items")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := tx.Create(&orderDB).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to insert order")
|
||||||
|
}
|
||||||
|
|
||||||
|
order.ID = orderDB.ID
|
||||||
|
}
|
||||||
|
|
||||||
for i := range order.OrderItems {
|
for i := range order.OrderItems {
|
||||||
item := &order.OrderItems[i]
|
item := &order.OrderItems[i]
|
||||||
item.OrderID = orderDB.ID
|
item.OrderID = order.ID
|
||||||
|
|
||||||
itemDB := r.toOrderItemDBModel(item)
|
itemDB := r.toOrderItemDBModel(item)
|
||||||
|
|
||||||
@ -81,23 +98,19 @@ func (r *orderRepository) Create(ctx mycontext.Context, order *entity.Order) (*e
|
|||||||
item.ID = itemDB.ID
|
item.ID = itemDB.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if order.InProgressOrderID != 0 {
|
// Commit the transaction
|
||||||
if err := tx.Where("order_id = ?", order.InProgressOrderID).Delete(&models.OrderItemDB{}).Error; err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
return nil, errors.Wrap(err, "failed to delete in-progress order items")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tx.Where("id = ?", order.InProgressOrderID).Delete(&models.OrderDB{}).Error; err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
return nil, errors.Wrap(err, "failed to delete in-progress order")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tx.Commit().Error; err != nil {
|
if err := tx.Commit().Error; err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to commit transaction")
|
return nil, errors.Wrap(err, "failed to commit transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
return order, nil
|
var updatedOrderDB models.OrderDB
|
||||||
|
if err := r.db.Preload("OrderItems").First(&updatedOrderDB, order.ID).Error; err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to fetch updated order")
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedOrder := r.toDomainOrderModel(&updatedOrderDB)
|
||||||
|
|
||||||
|
return updatedOrder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *orderRepository) FindByID(ctx mycontext.Context, id int64) (*entity.Order, error) {
|
func (r *orderRepository) FindByID(ctx mycontext.Context, id int64) (*entity.Order, error) {
|
||||||
@ -690,7 +703,7 @@ func (r *orderRepository) GetSalesByCategory(
|
|||||||
ctx mycontext.Context,
|
ctx mycontext.Context,
|
||||||
req entity.SalesByCategoryRequest,
|
req entity.SalesByCategoryRequest,
|
||||||
) ([]entity.SalesByCategoryItem, error) {
|
) ([]entity.SalesByCategoryItem, error) {
|
||||||
var salesByCategory []entity.SalesByCategoryItem
|
salesByCategory := []entity.SalesByCategoryItem{}
|
||||||
|
|
||||||
baseQuery := r.db.Model(&models.OrderItemDB{}).
|
baseQuery := r.db.Model(&models.OrderItemDB{}).
|
||||||
Joins("JOIN orders ON order_items.order_id = orders.id").
|
Joins("JOIN orders ON order_items.order_id = orders.id").
|
||||||
@ -792,8 +805,7 @@ func (r *orderRepository) GetPopularProducts(
|
|||||||
return nil, errors.Wrap(err, "failed to calculate total sales")
|
return nil, errors.Wrap(err, "failed to calculate total sales")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the query for popular products
|
popularProducts := []entity.PopularProductItem{}
|
||||||
var popularProducts []entity.PopularProductItem
|
|
||||||
orderClause := "total_sales DESC"
|
orderClause := "total_sales DESC"
|
||||||
if req.SortBy == "revenue" {
|
if req.SortBy == "revenue" {
|
||||||
orderClause = "total_revenue DESC"
|
orderClause = "total_revenue DESC"
|
||||||
@ -824,3 +836,23 @@ func (r *orderRepository) GetPopularProducts(
|
|||||||
|
|
||||||
return popularProducts, nil
|
return popularProducts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *orderRepository) FindByIDAndPartnerID(ctx mycontext.Context, id int64, partnerID int64) (*entity.Order, error) {
|
||||||
|
var orderDB models.OrderDB
|
||||||
|
|
||||||
|
if err := r.db.Preload("OrderItems").Where("id = ? AND partner_id = ?", id, partnerID).First(&orderDB).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, errors.New("order not found")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "failed to find order")
|
||||||
|
}
|
||||||
|
|
||||||
|
order := r.toDomainOrderModel(&orderDB)
|
||||||
|
|
||||||
|
for _, itemDB := range orderDB.OrderItems {
|
||||||
|
item := r.toDomainOrderItemModel(&itemDB)
|
||||||
|
order.OrderItems = append(order.OrderItems, *item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return order, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
type ProductRepository interface {
|
type ProductRepository interface {
|
||||||
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
|
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
|
||||||
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
|
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
|
||||||
|
GetProductsByPartnerID(ctx mycontext.Context, req entity.ProductSearch) ([]*entity.Product, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type productRepository struct {
|
type productRepository struct {
|
||||||
@ -78,5 +79,40 @@ func (r *productRepository) toDomainProductModel(dbModel *models.ProductDB) *ent
|
|||||||
Status: dbModel.Status,
|
Status: dbModel.Status,
|
||||||
CreatedAt: dbModel.CreatedAt,
|
CreatedAt: dbModel.CreatedAt,
|
||||||
UpdatedAt: dbModel.UpdatedAt,
|
UpdatedAt: dbModel.UpdatedAt,
|
||||||
|
Image: dbModel.Image,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *productRepository) GetProductsByPartnerID(ctx mycontext.Context, req entity.ProductSearch) ([]*entity.Product, int64, error) {
|
||||||
|
if req.PartnerID == 0 {
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
query := r.db.Where("partner_id = ?", req.PartnerID)
|
||||||
|
|
||||||
|
if req.Type != "" {
|
||||||
|
query = query.Where("type = ?", req.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Name != "" {
|
||||||
|
query = query.Where("name ILIKE ?", "%"+req.Name+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
if err := query.Model(&models.ProductDB{}).Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, errors.Wrap(err, "failed to count products")
|
||||||
|
}
|
||||||
|
|
||||||
|
var productsDB []models.ProductDB
|
||||||
|
|
||||||
|
if err := query.Find(&productsDB).Error; err != nil {
|
||||||
|
return nil, 0, errors.Wrap(err, "failed to find products")
|
||||||
|
}
|
||||||
|
|
||||||
|
products := make([]*entity.Product, 0, len(productsDB))
|
||||||
|
for i := range productsDB {
|
||||||
|
product := r.toDomainProductModel(&productsDB[i])
|
||||||
|
products = append(products, product)
|
||||||
|
}
|
||||||
|
|
||||||
|
return products, total, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -120,6 +120,8 @@ type Crypto interface {
|
|||||||
ParseAndValidateJWT(token string) (*entity.JWTAuthClaims, error)
|
ParseAndValidateJWT(token string) (*entity.JWTAuthClaims, error)
|
||||||
GenerateJWTWithdraw(req *entity.WalletWithdrawRequest) (string, error)
|
GenerateJWTWithdraw(req *entity.WalletWithdrawRequest) (string, error)
|
||||||
ValidateJWTWithdraw(tokenString string) (*entity.WalletWithdrawRequest, error)
|
ValidateJWTWithdraw(tokenString string) (*entity.WalletWithdrawRequest, error)
|
||||||
|
GenerateJWTCustomer(user *entity.Customer) (string, error)
|
||||||
|
ParseAndValidateJWTCustomer(tokenString string) (*entity.JWTAuthClaimsCustomer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type User interface {
|
type User interface {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"enaklo-pos-be/internal/handlers/http"
|
||||||
"enaklo-pos-be/internal/handlers/http/customerauth"
|
"enaklo-pos-be/internal/handlers/http/customerauth"
|
||||||
"enaklo-pos-be/internal/handlers/http/discovery"
|
"enaklo-pos-be/internal/handlers/http/discovery"
|
||||||
"enaklo-pos-be/internal/middlewares"
|
"enaklo-pos-be/internal/middlewares"
|
||||||
@ -14,11 +15,12 @@ func RegisterCustomerRoutes(app *app.Server, serviceManager *services.ServiceMan
|
|||||||
repoManager *repository.RepoManagerImpl) {
|
repoManager *repository.RepoManagerImpl) {
|
||||||
approute := app.Group("/api/v1/customer")
|
approute := app.Group("/api/v1/customer")
|
||||||
|
|
||||||
authMiddleware := middlewares.AuthorizationMiddleware(repoManager.Crypto)
|
authMiddleware := middlewares.CustomerAuthorizationMiddleware(repoManager.Crypto)
|
||||||
|
|
||||||
serverRoutes := []HTTPHandlerRoutes{
|
serverRoutes := []HTTPHandlerRoutes{
|
||||||
discovery.NewHandler(serviceManager.DiscoverService),
|
discovery.NewHandler(serviceManager.DiscoverService),
|
||||||
customerauth.NewAuthHandler(serviceManager.AuthSvc, serviceManager.UserSvc, serviceManager.CustomerV2Svc),
|
customerauth.NewAuthHandler(serviceManager.AuthV2Svc),
|
||||||
|
http.NewMenuHandler(serviceManager.ProductV2Svc, serviceManager.InProgressSvc),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, handler := range serverRoutes {
|
for _, handler := range serverRoutes {
|
||||||
|
|||||||
@ -14,9 +14,11 @@ import (
|
|||||||
"enaklo-pos-be/internal/services/studio"
|
"enaklo-pos-be/internal/services/studio"
|
||||||
"enaklo-pos-be/internal/services/transaction"
|
"enaklo-pos-be/internal/services/transaction"
|
||||||
"enaklo-pos-be/internal/services/users"
|
"enaklo-pos-be/internal/services/users"
|
||||||
|
authSvc "enaklo-pos-be/internal/services/v2/auth"
|
||||||
customerSvc "enaklo-pos-be/internal/services/v2/customer"
|
customerSvc "enaklo-pos-be/internal/services/v2/customer"
|
||||||
"enaklo-pos-be/internal/services/v2/inprogress_order"
|
"enaklo-pos-be/internal/services/v2/inprogress_order"
|
||||||
orderSvc "enaklo-pos-be/internal/services/v2/order"
|
orderSvc "enaklo-pos-be/internal/services/v2/order"
|
||||||
|
|
||||||
"enaklo-pos-be/internal/services/v2/partner_settings"
|
"enaklo-pos-be/internal/services/v2/partner_settings"
|
||||||
productSvc "enaklo-pos-be/internal/services/v2/product"
|
productSvc "enaklo-pos-be/internal/services/v2/product"
|
||||||
|
|
||||||
@ -48,6 +50,7 @@ type ServiceManagerImpl struct {
|
|||||||
ProductV2Svc productSvc.Service
|
ProductV2Svc productSvc.Service
|
||||||
MemberRegistrationSvc member.RegistrationService
|
MemberRegistrationSvc member.RegistrationService
|
||||||
InProgressSvc inprogress_order.InProgressOrderService
|
InProgressSvc inprogress_order.InProgressOrderService
|
||||||
|
AuthV2Svc authSvc.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
|
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
|
||||||
@ -76,6 +79,8 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
|
|||||||
MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2),
|
MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2),
|
||||||
CustomerV2Svc: custSvcV2,
|
CustomerV2Svc: custSvcV2,
|
||||||
InProgressSvc: inprogressOrder,
|
InProgressSvc: inprogressOrder,
|
||||||
|
ProductV2Svc: productSvcV2,
|
||||||
|
AuthV2Svc: authSvc.New(repo.CustomerRepo, repo.Crypto),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
58
internal/services/v2/auth/auth.go
Normal file
58
internal/services/v2/auth/auth.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"enaklo-pos-be/internal/common/errors"
|
||||||
|
"enaklo-pos-be/internal/common/logger"
|
||||||
|
"enaklo-pos-be/internal/common/mycontext"
|
||||||
|
"enaklo-pos-be/internal/entity"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CryptoSvc interface {
|
||||||
|
GenerateJWTCustomer(user *entity.Customer) (string, error)
|
||||||
|
CompareHashAndPassword(hash string, password string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
AuthCustomer(ctx mycontext.Context, email, password string) (*entity.AuthenticateUser, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type authSvc struct {
|
||||||
|
repo Repository
|
||||||
|
crypt CryptoSvc
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(repo Repository, cryptSvc CryptoSvc) Service {
|
||||||
|
return &authSvc{
|
||||||
|
repo: repo,
|
||||||
|
crypt: cryptSvc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a authSvc) AuthCustomer(ctx mycontext.Context, email, password string) (*entity.AuthenticateUser, error) {
|
||||||
|
user, err := a.repo.FindByEmail(ctx, email)
|
||||||
|
if err != nil {
|
||||||
|
logger.ContextLogger(ctx).Error("error when get user", zap.Error(err))
|
||||||
|
return nil, errors.ErrorInternalServer
|
||||||
|
}
|
||||||
|
|
||||||
|
if user == nil {
|
||||||
|
return nil, errors.ErrorUserIsNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := a.crypt.CompareHashAndPassword(user.Password, password); !ok {
|
||||||
|
return nil, errors.ErrorUserInvalidLogin
|
||||||
|
}
|
||||||
|
|
||||||
|
signedToken, err := a.crypt.GenerateJWTCustomer(user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.ToUserAuthenticate(signedToken), nil
|
||||||
|
}
|
||||||
@ -13,11 +13,13 @@ import (
|
|||||||
type InProgressOrderService interface {
|
type InProgressOrderService interface {
|
||||||
Save(ctx mycontext.Context, order *entity.OrderRequest) (*entity.Order, error)
|
Save(ctx mycontext.Context, order *entity.OrderRequest) (*entity.Order, error)
|
||||||
GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.Order, error)
|
GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.Order, error)
|
||||||
|
GetOrderByOrderAndPartnerID(ctx mycontext.Context, partnerID int64, orderID int64) (*entity.Order, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderRepository interface {
|
type OrderRepository interface {
|
||||||
CreateOrUpdate(ctx mycontext.Context, order *entity.Order) (*entity.Order, error)
|
CreateOrUpdate(ctx mycontext.Context, order *entity.Order) (*entity.Order, error)
|
||||||
GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int, status string) ([]*entity.Order, error)
|
GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int, status string) ([]*entity.Order, error)
|
||||||
|
FindByIDAndPartnerID(ctx mycontext.Context, id int64, partnerID int64) (*entity.Order, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderCalculator interface {
|
type OrderCalculator interface {
|
||||||
@ -26,6 +28,7 @@ type OrderCalculator interface {
|
|||||||
items []entity.OrderItemRequest,
|
items []entity.OrderItemRequest,
|
||||||
productDetails *entity.ProductDetails,
|
productDetails *entity.ProductDetails,
|
||||||
source string,
|
source string,
|
||||||
|
partnerID int64,
|
||||||
) (*entity.OrderCalculation, error)
|
) (*entity.OrderCalculation, error)
|
||||||
ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error)
|
ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error)
|
||||||
}
|
}
|
||||||
@ -57,7 +60,7 @@ func (s *inProgressOrderSvc) Save(ctx mycontext.Context, req *entity.OrderReques
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
orderCalculation, err := s.orderCalculator.CalculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source)
|
orderCalculation, err := s.orderCalculator.CalculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source, req.PartnerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -71,11 +74,12 @@ func (s *inProgressOrderSvc) Save(ctx mycontext.Context, req *entity.OrderReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
orderItems[i] = entity.OrderItem{
|
orderItems[i] = entity.OrderItem{
|
||||||
ItemID: item.ProductID,
|
ItemID: item.ProductID,
|
||||||
ItemName: productName,
|
ItemName: productName,
|
||||||
Quantity: item.Quantity,
|
Quantity: item.Quantity,
|
||||||
Price: product.Price,
|
Price: product.Price,
|
||||||
ItemType: product.Type,
|
ItemType: product.Type,
|
||||||
|
Description: product.Description,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,3 +123,15 @@ func (s *inProgressOrderSvc) GetOrdersByPartnerID(ctx mycontext.Context, partner
|
|||||||
|
|
||||||
return orders, nil
|
return orders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *inProgressOrderSvc) GetOrderByOrderAndPartnerID(ctx mycontext.Context, partnerID int64, orderID int64) (*entity.Order, error) {
|
||||||
|
orders, err := s.repo.FindByIDAndPartnerID(ctx, orderID, partnerID)
|
||||||
|
if err != nil {
|
||||||
|
logger.ContextLogger(ctx).Error("failed to get in-progress orders by partner ID",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.Int64("partnerID", partnerID))
|
||||||
|
return nil, errors.Wrap(err, "failed to get order")
|
||||||
|
}
|
||||||
|
|
||||||
|
return orders, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
orderCalculation, err := s.CalculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source)
|
orderCalculation, err := s.CalculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source, req.PartnerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -104,6 +104,7 @@ func (s *orderSvc) CalculateOrderTotals(
|
|||||||
items []entity.OrderItemRequest,
|
items []entity.OrderItemRequest,
|
||||||
productDetails *entity.ProductDetails,
|
productDetails *entity.ProductDetails,
|
||||||
source string,
|
source string,
|
||||||
|
partnerID int64,
|
||||||
) (*entity.OrderCalculation, error) {
|
) (*entity.OrderCalculation, error) {
|
||||||
subtotal := 0.0
|
subtotal := 0.0
|
||||||
|
|
||||||
@ -115,8 +116,7 @@ func (s *orderSvc) CalculateOrderTotals(
|
|||||||
subtotal += product.Price * float64(item.Quantity)
|
subtotal += product.Price * float64(item.Quantity)
|
||||||
}
|
}
|
||||||
|
|
||||||
partnerID := ctx.GetPartnerID()
|
setting, err := s.partnerSetting.GetSettings(ctx, partnerID)
|
||||||
setting, err := s.partnerSetting.GetSettings(ctx, *partnerID)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.NewError(errors.ErrorInvalidRequest.ErrorType(), "failed to get partner settings")
|
return nil, errors.NewError(errors.ErrorInvalidRequest.ErrorType(), "failed to get partner settings")
|
||||||
|
|||||||
@ -67,6 +67,7 @@ type Service interface {
|
|||||||
items []entity.OrderItemRequest,
|
items []entity.OrderItemRequest,
|
||||||
productDetails *entity.ProductDetails,
|
productDetails *entity.ProductDetails,
|
||||||
source string,
|
source string,
|
||||||
|
partnerID int64,
|
||||||
) (*entity.OrderCalculation, error)
|
) (*entity.OrderCalculation, error)
|
||||||
ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error)
|
ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error)
|
||||||
GetOrderPaymentAnalysis(
|
GetOrderPaymentAnalysis(
|
||||||
|
|||||||
@ -31,3 +31,13 @@ func (s *productSvc) GetProductsByIDs(ctx mycontext.Context, ids []int64, partne
|
|||||||
|
|
||||||
return products, nil
|
return products, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *productSvc) GetProductsByPartnerID(ctx mycontext.Context, search entity.ProductSearch) ([]*entity.Product, int64, error) {
|
||||||
|
products, total, err := s.repo.GetProductsByPartnerID(ctx, search)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, errors.Wrap(err, "failed to get products by partner ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
return products, total, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -8,11 +8,13 @@ import (
|
|||||||
type Repository interface {
|
type Repository interface {
|
||||||
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
|
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
|
||||||
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
|
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
|
||||||
|
GetProductsByPartnerID(ctx mycontext.Context, req entity.ProductSearch) ([]*entity.Product, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
|
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
|
||||||
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
|
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
|
||||||
|
GetProductsByPartnerID(ctx mycontext.Context, search entity.ProductSearch) ([]*entity.Product, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type productSvc struct {
|
type productSvc struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user