Update Tranaction
This commit is contained in:
parent
4c1a819365
commit
a4bad0c088
@ -25,7 +25,7 @@ postgresql:
|
||||
max-idle-connections-in-second: 600
|
||||
max-open-connections-in-second: 600
|
||||
connection-max-life-time-in-second: 600
|
||||
debug: false
|
||||
debug: true
|
||||
|
||||
oss:
|
||||
access_key_id: e50b31e5eddf63c0ZKB2
|
||||
|
||||
@ -17,21 +17,23 @@ const (
|
||||
errUserIsNotFound ErrType = "User is not found"
|
||||
errInvalidLogin ErrType = "User email or password is invalid"
|
||||
errUnauthorized ErrType = "Unauthorized"
|
||||
errInsufficientBalance ErrType = "Insufficient Balance"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorBadRequest = NewServiceException(errBadRequest)
|
||||
ErrorInvalidRequest = NewServiceException(errInvalidRequest)
|
||||
ErrorUnauthorized = NewServiceException(errUnauthorized)
|
||||
ErrorOrderNotFound = NewServiceException(errOrderNotFound)
|
||||
ErrorClientIDNotDefined = NewServiceException(errCheckoutIDNotDefined)
|
||||
ErrorRequestTimeout = NewServiceException(errRequestTimeOut)
|
||||
ErrorExternalCall = NewServiceException(errExternalCall)
|
||||
ErrorFailedExternalCall = NewServiceException(errFailedExternalCall)
|
||||
ErrorConnectionTimeOut = NewServiceException(errConnectTimeOut)
|
||||
ErrorInternalServer = NewServiceException(errInternalServer)
|
||||
ErrorUserIsNotFound = NewServiceException(errUserIsNotFound)
|
||||
ErrorUserInvalidLogin = NewServiceException(errInvalidLogin)
|
||||
ErrorBadRequest = NewServiceException(errBadRequest)
|
||||
ErrorInvalidRequest = NewServiceException(errInvalidRequest)
|
||||
ErrorUnauthorized = NewServiceException(errUnauthorized)
|
||||
ErrorOrderNotFound = NewServiceException(errOrderNotFound)
|
||||
ErrorClientIDNotDefined = NewServiceException(errCheckoutIDNotDefined)
|
||||
ErrorRequestTimeout = NewServiceException(errRequestTimeOut)
|
||||
ErrorExternalCall = NewServiceException(errExternalCall)
|
||||
ErrorFailedExternalCall = NewServiceException(errFailedExternalCall)
|
||||
ErrorConnectionTimeOut = NewServiceException(errConnectTimeOut)
|
||||
ErrorInternalServer = NewServiceException(errInternalServer)
|
||||
ErrorUserIsNotFound = NewServiceException(errUserIsNotFound)
|
||||
ErrorUserInvalidLogin = NewServiceException(errInvalidLogin)
|
||||
ErrorInsufficientBalance = NewServiceException(errInsufficientBalance)
|
||||
)
|
||||
|
||||
type Error interface {
|
||||
@ -105,6 +107,9 @@ func (s *ServiceException) MapErrorsToCode() Code {
|
||||
case errInvalidLogin:
|
||||
return BadRequest
|
||||
|
||||
case errInsufficientBalance:
|
||||
return BadRequest
|
||||
|
||||
default:
|
||||
return BadRequest
|
||||
}
|
||||
|
||||
@ -50,3 +50,8 @@ type TransactionList struct {
|
||||
PartnerName string
|
||||
Amount int64
|
||||
}
|
||||
|
||||
type TransactionApproval struct {
|
||||
TransactionID string
|
||||
Status string
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ func (h *TransactionHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc)
|
||||
route := group.Group("/transaction")
|
||||
|
||||
route.GET("/search", jwt, h.Search)
|
||||
route.POST("/approval", jwt, h.Approval)
|
||||
}
|
||||
|
||||
func New(service services.Transaction) *TransactionHandler {
|
||||
@ -63,6 +64,36 @@ func (h *TransactionHandler) Search(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func (h *TransactionHandler) Approval(c *gin.Context) {
|
||||
var req request.ApprovalRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||
return
|
||||
}
|
||||
ctx := request.GetMyContext(c)
|
||||
|
||||
if !ctx.IsAdmin() {
|
||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
response.ErrorWrapper(c, errors.NewError(errors.ErrorBadRequest.ErrorType(), err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
err := h.service.Approval(ctx, req.ToEntity())
|
||||
if err != nil {
|
||||
response.ErrorWrapper(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.BaseResponse{
|
||||
Success: true,
|
||||
Status: http.StatusOK,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *TransactionHandler) ToTransactionListResponse(transactions []*entity.TransactionList, totalCount, limit, offset int) response.TransactionListResponse {
|
||||
responseItems := make([]response.TransactionListItem, len(transactions))
|
||||
for i, transaction := range transactions {
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"furtuna-be/internal/common/mycontext"
|
||||
"furtuna-be/internal/constants/transaction"
|
||||
"furtuna-be/internal/entity"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type Transaction struct {
|
||||
@ -19,9 +20,41 @@ type TransactionSearch struct {
|
||||
Type string `form:"type,default="`
|
||||
}
|
||||
|
||||
type ApprovalRequest struct {
|
||||
Status string `json:"status" validate:"required,approvalStatus"`
|
||||
TransactionID string `json:"transaction_id" validate:"required"`
|
||||
}
|
||||
|
||||
func (a *ApprovalRequest) ToEntity() *entity.TransactionApproval {
|
||||
return &entity.TransactionApproval{
|
||||
Status: a.Status,
|
||||
TransactionID: a.TransactionID,
|
||||
}
|
||||
}
|
||||
|
||||
func approvalStatus(fl validator.FieldLevel) bool {
|
||||
status := fl.Field().String()
|
||||
return status == "APPROVE" || status == "REJECT"
|
||||
}
|
||||
|
||||
func (a *ApprovalRequest) Validate() error {
|
||||
validate := validator.New()
|
||||
err := validate.RegisterValidation("approvalStatus", approvalStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.Struct(a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TransactionSearch) ToEntity(ctx mycontext.Context) entity.TransactionSearch {
|
||||
return entity.TransactionSearch{
|
||||
PartnerID: ctx.GetPartnerID(),
|
||||
SiteID: ctx.GetSiteID(),
|
||||
Type: t.Type,
|
||||
Status: t.Status,
|
||||
Limit: t.Limit,
|
||||
|
||||
@ -208,6 +208,8 @@ type License interface {
|
||||
}
|
||||
|
||||
type TransactionRepository interface {
|
||||
FindByID(ctx context.Context, id string) (*entity.Transaction, error)
|
||||
Create(ctx context.Context, trx *gorm.DB, transaction *entity.Transaction) (*entity.Transaction, error)
|
||||
GetTransactionList(ctx mycontext.Context, req entity.TransactionSearch) ([]*entity.TransactionList, int, error)
|
||||
Update(ctx context.Context, trx *gorm.DB, transaction *entity.Transaction) (*entity.Transaction, error)
|
||||
}
|
||||
|
||||
@ -37,8 +37,8 @@ func (r *TransactionRepository) Create(ctx context.Context, trx *gorm.DB, transa
|
||||
}
|
||||
|
||||
// Update updates an existing transaction in the database.
|
||||
func (r *TransactionRepository) Update(ctx context.Context, transaction *entity.Transaction) (*entity.Transaction, error) {
|
||||
if err := r.db.WithContext(ctx).Save(transaction).Error; err != nil {
|
||||
func (r *TransactionRepository) Update(ctx context.Context, trx *gorm.DB, transaction *entity.Transaction) (*entity.Transaction, error) {
|
||||
if err := trx.WithContext(ctx).Save(transaction).Error; err != nil {
|
||||
zap.L().Error("error when updating transaction", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
@ -137,22 +137,30 @@ func (r *TransactionRepository) GetTransactionList(ctx mycontext.Context, req en
|
||||
query := r.db.Table("transactions t").
|
||||
Select("t.id, t.transaction_type, t.status, t.created_at, s.name as site_name, p.name as partner_name, t.amount").
|
||||
Joins("left join sites s on t.site_id = s.id").
|
||||
Joins("left join partners p on t.partner_id = p.id").
|
||||
Where("t.partner_id = ?", req.PartnerID)
|
||||
Joins("left join partners p on t.partner_id = p.id")
|
||||
|
||||
if req.SiteID != nil {
|
||||
query = query.Where("t.site_id = ?", req.SiteID)
|
||||
}
|
||||
|
||||
if req.Type != "" {
|
||||
query = query.Where("t.transaction_type = ?", req.Type)
|
||||
}
|
||||
|
||||
if req.Status != "" {
|
||||
query = query.Where("t.status = ?", req.Status)
|
||||
}
|
||||
|
||||
if req.Date != "" {
|
||||
query = query.Where("DATE(t.created_at) = ?", req.Date)
|
||||
}
|
||||
|
||||
if req.PartnerID != nil {
|
||||
query = query.Where("t.partner_id = ?", req.PartnerID)
|
||||
}
|
||||
|
||||
query = query.Order("t.created_at DESC")
|
||||
|
||||
query = query.Count(&total)
|
||||
|
||||
if req.Offset > 0 {
|
||||
|
||||
@ -54,7 +54,7 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
|
||||
repo.Partner, users.NewUserService(repo.User, repo.Branch), repo.Trx, repo.Wallet),
|
||||
SiteSvc: site.NewSiteService(repo.Site),
|
||||
LicenseSvc: service.NewLicenseService(repo.License),
|
||||
Transaction: transaction.New(repo.Transaction),
|
||||
Transaction: transaction.New(repo.Transaction, repo.Wallet, repo.Trx),
|
||||
Balance: balance.NewBalanceService(repo.Wallet, repo.Trx, repo.Crypto, &cfg.Withdraw, repo.Transaction),
|
||||
}
|
||||
}
|
||||
@ -146,6 +146,7 @@ type License interface {
|
||||
|
||||
type Transaction interface {
|
||||
GetTransactionList(ctx mycontext.Context, req entity.TransactionSearch) ([]*entity.TransactionList, int, error)
|
||||
Approval(ctx mycontext.Context, req *entity.TransactionApproval) error
|
||||
}
|
||||
|
||||
type Balance interface {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package transaction
|
||||
|
||||
import (
|
||||
errors2 "furtuna-be/internal/common/errors"
|
||||
"furtuna-be/internal/common/logger"
|
||||
"furtuna-be/internal/common/mycontext"
|
||||
"furtuna-be/internal/entity"
|
||||
@ -10,12 +11,19 @@ import (
|
||||
)
|
||||
|
||||
type TransactionService struct {
|
||||
repo repository.TransactionRepository
|
||||
repo repository.TransactionRepository
|
||||
wallet repository.WalletRepository
|
||||
trx repository.TransactionManager
|
||||
}
|
||||
|
||||
func New(repo repository.TransactionRepository) *TransactionService {
|
||||
func New(repo repository.TransactionRepository,
|
||||
wallet repository.WalletRepository,
|
||||
trx repository.TransactionManager,
|
||||
) *TransactionService {
|
||||
return &TransactionService{
|
||||
repo: repo,
|
||||
repo: repo,
|
||||
wallet: wallet,
|
||||
trx: trx,
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,3 +37,75 @@ func (s *TransactionService) GetTransactionList(ctx mycontext.Context,
|
||||
|
||||
return transactions, total, nil
|
||||
}
|
||||
|
||||
func (s *TransactionService) Approval(ctx mycontext.Context, req *entity.TransactionApproval) error {
|
||||
// Start a transaction
|
||||
trx, _ := s.trx.Begin(ctx)
|
||||
|
||||
// Retrieve the transaction by ID
|
||||
transaction, err := s.repo.FindByID(ctx, req.TransactionID)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when retrieving transaction by ID", zap.Error(err))
|
||||
trx.Rollback()
|
||||
return errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
if transaction.Status != "WAITING_APPROVAL" {
|
||||
return errors2.NewError(errors2.ErrorBadRequest.ErrorType(),
|
||||
"invalid state, transaction already approved or rejected")
|
||||
}
|
||||
|
||||
// Retrieve the wallet associated with the transaction's partner ID
|
||||
wallet, err := s.wallet.GetForUpdate(ctx, trx, transaction.PartnerID)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error retrieving wallet by partner ID", zap.Error(err))
|
||||
trx.Rollback()
|
||||
return errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
// Approve or Reject the transaction
|
||||
switch req.Status {
|
||||
case "APPROVE":
|
||||
if wallet.AuthBalance < transaction.Amount {
|
||||
trx.Rollback()
|
||||
return errors2.ErrorInsufficientBalance
|
||||
}
|
||||
wallet.AuthBalance -= transaction.Amount
|
||||
|
||||
case "REJECT":
|
||||
if wallet.AuthBalance < transaction.Amount {
|
||||
trx.Rollback()
|
||||
return errors2.ErrorInsufficientBalance
|
||||
}
|
||||
wallet.AuthBalance -= transaction.Amount
|
||||
wallet.Balance += transaction.Amount
|
||||
|
||||
default:
|
||||
trx.Rollback()
|
||||
return errors2.ErrorBadRequest
|
||||
}
|
||||
|
||||
// Update the wallet with the new balances
|
||||
if _, err := s.wallet.Update(ctx, trx, wallet); err != nil {
|
||||
logger.ContextLogger(ctx).Error("error updating wallet balance", zap.Error(err))
|
||||
trx.Rollback()
|
||||
return errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
// Update the transaction status and persist changes
|
||||
transaction.Status = req.Status
|
||||
transaction.UpdatedBy = ctx.RequestedBy()
|
||||
|
||||
if _, err := s.repo.Update(ctx, trx, transaction); err != nil {
|
||||
logger.ContextLogger(ctx).Error("error updating transaction status", zap.Error(err))
|
||||
trx.Rollback()
|
||||
return errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
if err := trx.Commit().Error; err != nil {
|
||||
logger.ContextLogger(ctx).Error("error committing transaction", zap.Error(err))
|
||||
return errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user