Update Tranaction
This commit is contained in:
parent
4c1a819365
commit
a4bad0c088
@ -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: false
|
debug: true
|
||||||
|
|
||||||
oss:
|
oss:
|
||||||
access_key_id: e50b31e5eddf63c0ZKB2
|
access_key_id: e50b31e5eddf63c0ZKB2
|
||||||
|
|||||||
@ -17,6 +17,7 @@ const (
|
|||||||
errUserIsNotFound ErrType = "User is not found"
|
errUserIsNotFound ErrType = "User is not found"
|
||||||
errInvalidLogin ErrType = "User email or password is invalid"
|
errInvalidLogin ErrType = "User email or password is invalid"
|
||||||
errUnauthorized ErrType = "Unauthorized"
|
errUnauthorized ErrType = "Unauthorized"
|
||||||
|
errInsufficientBalance ErrType = "Insufficient Balance"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -32,6 +33,7 @@ var (
|
|||||||
ErrorInternalServer = NewServiceException(errInternalServer)
|
ErrorInternalServer = NewServiceException(errInternalServer)
|
||||||
ErrorUserIsNotFound = NewServiceException(errUserIsNotFound)
|
ErrorUserIsNotFound = NewServiceException(errUserIsNotFound)
|
||||||
ErrorUserInvalidLogin = NewServiceException(errInvalidLogin)
|
ErrorUserInvalidLogin = NewServiceException(errInvalidLogin)
|
||||||
|
ErrorInsufficientBalance = NewServiceException(errInsufficientBalance)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Error interface {
|
type Error interface {
|
||||||
@ -105,6 +107,9 @@ func (s *ServiceException) MapErrorsToCode() Code {
|
|||||||
case errInvalidLogin:
|
case errInvalidLogin:
|
||||||
return BadRequest
|
return BadRequest
|
||||||
|
|
||||||
|
case errInsufficientBalance:
|
||||||
|
return BadRequest
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return BadRequest
|
return BadRequest
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,3 +50,8 @@ type TransactionList struct {
|
|||||||
PartnerName string
|
PartnerName string
|
||||||
Amount int64
|
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 := group.Group("/transaction")
|
||||||
|
|
||||||
route.GET("/search", jwt, h.Search)
|
route.GET("/search", jwt, h.Search)
|
||||||
|
route.POST("/approval", jwt, h.Approval)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(service services.Transaction) *TransactionHandler {
|
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 {
|
func (h *TransactionHandler) ToTransactionListResponse(transactions []*entity.TransactionList, totalCount, limit, offset int) response.TransactionListResponse {
|
||||||
responseItems := make([]response.TransactionListItem, len(transactions))
|
responseItems := make([]response.TransactionListItem, len(transactions))
|
||||||
for i, transaction := range transactions {
|
for i, transaction := range transactions {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"furtuna-be/internal/common/mycontext"
|
"furtuna-be/internal/common/mycontext"
|
||||||
"furtuna-be/internal/constants/transaction"
|
"furtuna-be/internal/constants/transaction"
|
||||||
"furtuna-be/internal/entity"
|
"furtuna-be/internal/entity"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
@ -19,9 +20,41 @@ type TransactionSearch struct {
|
|||||||
Type string `form:"type,default="`
|
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 {
|
func (t *TransactionSearch) ToEntity(ctx mycontext.Context) entity.TransactionSearch {
|
||||||
return entity.TransactionSearch{
|
return entity.TransactionSearch{
|
||||||
PartnerID: ctx.GetPartnerID(),
|
PartnerID: ctx.GetPartnerID(),
|
||||||
|
SiteID: ctx.GetSiteID(),
|
||||||
Type: t.Type,
|
Type: t.Type,
|
||||||
Status: t.Status,
|
Status: t.Status,
|
||||||
Limit: t.Limit,
|
Limit: t.Limit,
|
||||||
|
|||||||
@ -208,6 +208,8 @@ type License interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TransactionRepository 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)
|
Create(ctx context.Context, trx *gorm.DB, transaction *entity.Transaction) (*entity.Transaction, error)
|
||||||
GetTransactionList(ctx mycontext.Context, req entity.TransactionSearch) ([]*entity.TransactionList, int, 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.
|
// Update updates an existing transaction in the database.
|
||||||
func (r *TransactionRepository) Update(ctx context.Context, transaction *entity.Transaction) (*entity.Transaction, error) {
|
func (r *TransactionRepository) Update(ctx context.Context, trx *gorm.DB, transaction *entity.Transaction) (*entity.Transaction, error) {
|
||||||
if err := r.db.WithContext(ctx).Save(transaction).Error; err != nil {
|
if err := trx.WithContext(ctx).Save(transaction).Error; err != nil {
|
||||||
zap.L().Error("error when updating transaction", zap.Error(err))
|
zap.L().Error("error when updating transaction", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -137,22 +137,30 @@ func (r *TransactionRepository) GetTransactionList(ctx mycontext.Context, req en
|
|||||||
query := r.db.Table("transactions t").
|
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").
|
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 sites s on t.site_id = s.id").
|
||||||
Joins("left join partners p on t.partner_id = p.id").
|
Joins("left join partners p on t.partner_id = p.id")
|
||||||
Where("t.partner_id = ?", req.PartnerID)
|
|
||||||
|
|
||||||
if req.SiteID != nil {
|
if req.SiteID != nil {
|
||||||
query = query.Where("t.site_id = ?", req.SiteID)
|
query = query.Where("t.site_id = ?", req.SiteID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Type != "" {
|
if req.Type != "" {
|
||||||
query = query.Where("t.transaction_type = ?", req.Type)
|
query = query.Where("t.transaction_type = ?", req.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Status != "" {
|
if req.Status != "" {
|
||||||
query = query.Where("t.status = ?", req.Status)
|
query = query.Where("t.status = ?", req.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Date != "" {
|
if req.Date != "" {
|
||||||
query = query.Where("DATE(t.created_at) = ?", 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)
|
query = query.Count(&total)
|
||||||
|
|
||||||
if req.Offset > 0 {
|
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),
|
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),
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,6 +146,7 @@ type License interface {
|
|||||||
|
|
||||||
type Transaction interface {
|
type Transaction interface {
|
||||||
GetTransactionList(ctx mycontext.Context, req entity.TransactionSearch) ([]*entity.TransactionList, int, error)
|
GetTransactionList(ctx mycontext.Context, req entity.TransactionSearch) ([]*entity.TransactionList, int, error)
|
||||||
|
Approval(ctx mycontext.Context, req *entity.TransactionApproval) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Balance interface {
|
type Balance interface {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
errors2 "furtuna-be/internal/common/errors"
|
||||||
"furtuna-be/internal/common/logger"
|
"furtuna-be/internal/common/logger"
|
||||||
"furtuna-be/internal/common/mycontext"
|
"furtuna-be/internal/common/mycontext"
|
||||||
"furtuna-be/internal/entity"
|
"furtuna-be/internal/entity"
|
||||||
@ -11,11 +12,18 @@ import (
|
|||||||
|
|
||||||
type TransactionService struct {
|
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{
|
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
|
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