fix: added wallet type to wallet and other minor fixes
This commit is contained in:
parent
972d62d568
commit
65bd5ab3f5
|
|
@ -133,6 +133,7 @@ CREATE TABLE IF NOT EXISTS wallets (
|
|||
is_bettable BOOLEAN NOT NULL,
|
||||
is_transferable BOOLEAN NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
type VARCHAR(255) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
|
|
@ -335,12 +336,16 @@ CREATE TABLE flags (
|
|||
reason TEXT,
|
||||
flagged_at TIMESTAMP DEFAULT NOW(),
|
||||
resolved BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- either bet or odd is flagged (not at the same time)
|
||||
CHECK (
|
||||
(bet_id IS NOT NULL AND odd_id IS NULL)
|
||||
OR
|
||||
(bet_id IS NULL AND odd_id IS NOT NULL)
|
||||
(
|
||||
bet_id IS NOT NULL
|
||||
AND odd_id IS NULL
|
||||
)
|
||||
OR (
|
||||
bet_id IS NULL
|
||||
AND odd_id IS NOT NULL
|
||||
)
|
||||
)
|
||||
);
|
||||
-- Views
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ VALUES ('addis_ababa', 'Addis Ababa'),
|
|||
('meki', 'Meki'),
|
||||
('negele_borana', 'Negele Borana'),
|
||||
('alaba_kulito', 'Alaba Kulito'),
|
||||
('alamata 14,', 'Alamata 14,'),
|
||||
('030', '030'),
|
||||
('alamata,', 'Alamata,'),
|
||||
('chiro', 'Chiro'),
|
||||
('tepi', 'Tepi'),
|
||||
('durame', 'Durame'),
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ INSERT INTO wallets (
|
|||
is_withdraw,
|
||||
is_bettable,
|
||||
is_transferable,
|
||||
user_id
|
||||
user_id,
|
||||
type
|
||||
)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING *;
|
||||
-- name: CreateCustomerWallet :one
|
||||
INSERT INTO customer_wallets (
|
||||
|
|
|
|||
|
|
@ -674,6 +674,7 @@ type Wallet struct {
|
|||
IsBettable bool `json:"is_bettable"`
|
||||
IsTransferable bool `json:"is_transferable"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Type string `json:"type"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
|
|
|
|||
|
|
@ -46,10 +46,11 @@ INSERT INTO wallets (
|
|||
is_withdraw,
|
||||
is_bettable,
|
||||
is_transferable,
|
||||
user_id
|
||||
user_id,
|
||||
type
|
||||
)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
`
|
||||
|
||||
type CreateWalletParams struct {
|
||||
|
|
@ -57,6 +58,7 @@ type CreateWalletParams struct {
|
|||
IsBettable bool `json:"is_bettable"`
|
||||
IsTransferable bool `json:"is_transferable"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wallet, error) {
|
||||
|
|
@ -65,6 +67,7 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal
|
|||
arg.IsBettable,
|
||||
arg.IsTransferable,
|
||||
arg.UserID,
|
||||
arg.Type,
|
||||
)
|
||||
var i Wallet
|
||||
err := row.Scan(
|
||||
|
|
@ -74,6 +77,7 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal
|
|||
&i.IsBettable,
|
||||
&i.IsTransferable,
|
||||
&i.UserID,
|
||||
&i.Type,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
|
|
@ -184,7 +188,7 @@ func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDet
|
|||
}
|
||||
|
||||
const GetAllWallets = `-- name: GetAllWallets :many
|
||||
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
FROM wallets
|
||||
`
|
||||
|
||||
|
|
@ -204,6 +208,7 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
|
|||
&i.IsBettable,
|
||||
&i.IsTransferable,
|
||||
&i.UserID,
|
||||
&i.Type,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
|
|
@ -314,7 +319,7 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (Cust
|
|||
}
|
||||
|
||||
const GetWalletByID = `-- name: GetWalletByID :one
|
||||
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
FROM wallets
|
||||
WHERE id = $1
|
||||
`
|
||||
|
|
@ -329,6 +334,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
|
|||
&i.IsBettable,
|
||||
&i.IsTransferable,
|
||||
&i.UserID,
|
||||
&i.Type,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
|
|
@ -340,7 +346,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
|
|||
}
|
||||
|
||||
const GetWalletByUserID = `-- name: GetWalletByUserID :many
|
||||
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
|
||||
FROM wallets
|
||||
WHERE user_id = $1
|
||||
`
|
||||
|
|
@ -361,6 +367,7 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet
|
|||
&i.IsBettable,
|
||||
&i.IsTransferable,
|
||||
&i.UserID,
|
||||
&i.Type,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ type CreateBetOutcomeReq struct {
|
|||
type CreateBetReq struct {
|
||||
Outcomes []CreateBetOutcomeReq `json:"outcomes" validate:"required"`
|
||||
Amount float32 `json:"amount" validate:"required,gt=0" example:"100.0"`
|
||||
BranchID *int64 `json:"branch_id,omitempty" validate:"required" example:"1"`
|
||||
BranchID *int64 `json:"branch_id,omitempty" example:"1"`
|
||||
}
|
||||
|
||||
type CreateBetWithFastCodeReq struct {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ type UpdateCompany struct {
|
|||
type CreateCompanyReq struct {
|
||||
Name string `json:"name" example:"CompanyName"`
|
||||
AdminID int64 `json:"admin_id" example:"1"`
|
||||
DeductedPercentage float32 `json:"deducted_percentage" example:"0.1" validate:"lt=1"`
|
||||
}
|
||||
type UpdateCompanyReq struct {
|
||||
Name *string `json:"name,omitempty" example:"CompanyName"`
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ type OtpProvider string
|
|||
|
||||
const (
|
||||
TwilioSms OtpProvider = "twilio"
|
||||
AfroMessage OtpProvider = "aformessage"
|
||||
AfroMessage OtpProvider = "afro_message"
|
||||
)
|
||||
|
||||
type Otp struct {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ type Wallet struct {
|
|||
IsTransferable bool
|
||||
IsActive bool
|
||||
UserID int64
|
||||
Type WalletType
|
||||
UpdatedAt time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
|
@ -63,6 +64,7 @@ type CreateWallet struct {
|
|||
IsBettable bool
|
||||
IsTransferable bool
|
||||
UserID int64
|
||||
Type WalletType
|
||||
}
|
||||
|
||||
type CreateCustomerWallet struct {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ func convertDBWallet(wallet dbgen.Wallet) domain.Wallet {
|
|||
IsTransferable: wallet.IsTransferable,
|
||||
IsActive: wallet.IsActive,
|
||||
UserID: wallet.UserID,
|
||||
Type: domain.WalletType(wallet.Type),
|
||||
UpdatedAt: wallet.UpdatedAt.Time,
|
||||
CreatedAt: wallet.CreatedAt.Time,
|
||||
}
|
||||
|
|
@ -28,6 +29,7 @@ func convertCreateWallet(wallet domain.CreateWallet) dbgen.CreateWalletParams {
|
|||
IsBettable: wallet.IsBettable,
|
||||
IsTransferable: wallet.IsTransferable,
|
||||
UserID: wallet.UserID,
|
||||
Type: string(wallet.Type),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,4 +277,3 @@ func (s *Store) GetTotalWallets(ctx context.Context, filter domain.ReportFilter)
|
|||
|
||||
return total, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ func (s *Service) SendOtp(ctx context.Context, sentTo string, otpFor domain.OtpF
|
|||
switch medium {
|
||||
case domain.OtpMediumSms:
|
||||
switch provider {
|
||||
case "twilio":
|
||||
case domain.TwilioSms:
|
||||
if err := s.SendTwilioSMSOTP(ctx, sentTo, message, provider); err != nil {
|
||||
return err
|
||||
}
|
||||
case "afromessage":
|
||||
case domain.AfroMessage:
|
||||
if err := s.SendAfroMessageSMSOTP(ctx, sentTo, message, provider); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ func (s *Service) SendTwilioSMSOTP(ctx context.Context, receiverPhone, message s
|
|||
|
||||
_, err := client.Api.CreateMessage(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s", "Error sending SMS message: %s" + err.Error())
|
||||
return fmt.Errorf("%s", "Error sending SMS message: %s"+err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -126,11 +126,29 @@ func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.
|
|||
if wallet.Balance < amount {
|
||||
// Send Wallet low to admin
|
||||
if walletType == domain.CompanyWalletType || walletType == domain.BranchWalletType {
|
||||
s.SendAdminWalletLowNotification(ctx, wallet, amount)
|
||||
s.SendAdminWalletInsufficientNotification(ctx, wallet, amount)
|
||||
}
|
||||
return domain.Transfer{}, ErrBalanceInsufficient
|
||||
}
|
||||
|
||||
if wallet.Type == domain.BranchWalletType || wallet.Type == domain.CompanyWalletType {
|
||||
var thresholds []float32
|
||||
|
||||
if wallet.Type == domain.CompanyWalletType {
|
||||
thresholds = []float32{100000, 50000, 25000, 10000, 5000, 3000, 1000, 500}
|
||||
} else {
|
||||
thresholds = []float32{5000, 3000, 1000, 500}
|
||||
}
|
||||
|
||||
balance := wallet.Balance.Float32()
|
||||
for _, threshold := range thresholds {
|
||||
if balance < threshold {
|
||||
s.SendAdminWalletLowNotification(ctx, wallet)
|
||||
break // only send once per check
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance-amount)
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -197,30 +215,28 @@ func (s *Service) UpdateWalletActive(ctx context.Context, id int64, isActive boo
|
|||
return s.walletStore.UpdateWalletActive(ctx, id, isActive)
|
||||
}
|
||||
|
||||
func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet, amount domain.Currency) error {
|
||||
func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet) error {
|
||||
// Send notification to admin team
|
||||
adminNotification := &domain.Notification{
|
||||
RecipientID: adminWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT,
|
||||
Level: domain.NotificationLevelError,
|
||||
Level: domain.NotificationLevelWarning,
|
||||
Reciever: domain.NotificationRecieverSideAdmin,
|
||||
DeliveryChannel: domain.DeliveryChannelEmail, // Or any preferred admin channel
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "CREDIT WARNING: System Running Out of Funds",
|
||||
Message: fmt.Sprintf(
|
||||
"Wallet ID %d has insufficient balance for transfer. Current balance: %.2f, Attempted transfer: %.2f",
|
||||
"Wallet ID %d is running low. Current balance: %.2f",
|
||||
adminWallet.ID,
|
||||
adminWallet.Balance.Float32(),
|
||||
amount.Float32(),
|
||||
),
|
||||
},
|
||||
Priority: 1, // High priority for admin alerts
|
||||
Metadata: fmt.Appendf(nil, `{
|
||||
"wallet_id": %d,
|
||||
"balance": %d,
|
||||
"required_amount": %d,
|
||||
"notification_type": "admin_alert"
|
||||
}`, adminWallet.ID, adminWallet.Balance, amount),
|
||||
}`, adminWallet.ID, adminWallet.Balance),
|
||||
}
|
||||
|
||||
// Get admin recipients and send to all
|
||||
|
|
@ -240,3 +256,85 @@ func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWalle
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, adminWallet domain.Wallet, amount domain.Currency) error {
|
||||
|
||||
|
||||
|
||||
|
||||
// Send notification to admin team
|
||||
adminNotification := &domain.Notification{
|
||||
RecipientID: adminWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT,
|
||||
Level: domain.NotificationLevelError,
|
||||
Reciever: domain.NotificationRecieverSideAdmin,
|
||||
DeliveryChannel: domain.DeliveryChannelEmail, // Or any preferred admin channel
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "CREDIT Error: Admin Wallet insufficient to process customer request",
|
||||
Message: fmt.Sprintf(
|
||||
"Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f",
|
||||
adminWallet.ID,
|
||||
amount.Float32(),
|
||||
adminWallet.Balance.Float32(),
|
||||
),
|
||||
},
|
||||
Priority: 1, // High priority for admin alerts
|
||||
Metadata: fmt.Appendf(nil, `{
|
||||
"wallet_id": %d,
|
||||
"balance": %d,
|
||||
"transaction amount": %.2f,
|
||||
"notification_type": "admin_alert"
|
||||
}`, adminWallet.ID, adminWallet.Balance, amount.Float32()),
|
||||
}
|
||||
|
||||
// Get admin recipients and send to all
|
||||
adminRecipients, err := s.notificationStore.ListRecipientIDs(ctx, domain.NotificationRecieverSideAdmin)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get admin recipients", "error", err)
|
||||
return err
|
||||
} else {
|
||||
for _, adminID := range adminRecipients {
|
||||
adminNotification.RecipientID = adminID
|
||||
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
|
||||
s.logger.Error("failed to send admin notification",
|
||||
"admin_id", adminID,
|
||||
"error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) SendCustomerWalletInsufficientNotification(ctx context.Context, customerWallet domain.Wallet, amount domain.Currency) error {
|
||||
// Send notification to admin team
|
||||
adminNotification := &domain.Notification{
|
||||
RecipientID: customerWallet.UserID,
|
||||
Type: domain.NOTIFICATION_TYPE_WALLET,
|
||||
Level: domain.NotificationLevelError,
|
||||
Reciever: domain.NotificationRecieverSideAdmin,
|
||||
DeliveryChannel: domain.DeliveryChannelEmail, // Or any preferred admin channel
|
||||
Payload: domain.NotificationPayload{
|
||||
Headline: "CREDIT Error: Admin Wallet insufficient to process customer request",
|
||||
Message: fmt.Sprintf(
|
||||
"Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f",
|
||||
customerWallet.ID,
|
||||
amount.Float32(),
|
||||
customerWallet.Balance.Float32(),
|
||||
),
|
||||
},
|
||||
Priority: 1, // High priority for admin alerts
|
||||
Metadata: fmt.Appendf(nil, `{
|
||||
"wallet_id": %d,
|
||||
"balance": %d,
|
||||
"transaction amount": %.2f,
|
||||
"notification_type": "admin_alert"
|
||||
}`, customerWallet.ID, customerWallet.Balance, amount.Float32()),
|
||||
}
|
||||
|
||||
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
|
||||
s.logger.Error("failed to send customer notification",
|
||||
"admin_id", customerWallet.UserID,
|
||||
"error", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,22 +24,22 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
|||
spec string
|
||||
task func()
|
||||
}{
|
||||
{
|
||||
spec: "0 0 * * * *", // Every 1 hour
|
||||
task: func() {
|
||||
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
|
||||
log.Printf("FetchUpcomingEvents error: %v", err)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
|
||||
task: func() {
|
||||
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
|
||||
log.Printf("FetchNonLiveOdds error: %v", err)
|
||||
}
|
||||
},
|
||||
},
|
||||
// {
|
||||
// spec: "0 0 * * * *", // Every 1 hour
|
||||
// task: func() {
|
||||
// if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
|
||||
// log.Printf("FetchUpcomingEvents error: %v", err)
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
|
||||
// task: func() {
|
||||
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
|
||||
// log.Printf("FetchNonLiveOdds error: %v", err)
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
{
|
||||
spec: "0 */5 * * * *", // Every 5 Minutes
|
||||
task: func() {
|
||||
|
|
@ -114,10 +114,10 @@ func SetupReportCronJobs(ctx context.Context, reportService *report.Service) {
|
|||
spec string
|
||||
period string
|
||||
}{
|
||||
{
|
||||
spec: "*/300 * * * * *", // Every 5 minutes (300 seconds)
|
||||
period: "5min",
|
||||
},
|
||||
// {
|
||||
// spec: "*/300 * * * * *", // Every 5 minutes (300 seconds)
|
||||
// period: "5min",
|
||||
// },
|
||||
{
|
||||
spec: "0 0 0 * * *", // Daily at midnight
|
||||
period: "daily",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import (
|
|||
|
||||
// loginCustomerReq represents the request body for the LoginCustomer endpoint.
|
||||
type loginCustomerReq struct {
|
||||
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
|
||||
Email string `json:"email" validate:"required_without=PhoneNumber" example:"john.doe@example.com"`
|
||||
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
|
||||
Password string `json:"password" validate:"required" example:"password123"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,21 +73,28 @@ func (h *Handler) GetDashboardReport(c *fiber.Ctx) error {
|
|||
func parseReportFilter(c *fiber.Ctx) (domain.ReportFilter, error) {
|
||||
var filter domain.ReportFilter
|
||||
var err error
|
||||
role := c.Locals("role").(domain.Role)
|
||||
|
||||
if c.Query("company_id") != "" && role == domain.RoleSuperAdmin {
|
||||
|
||||
if c.Query("company_id") != "" {
|
||||
companyID, err := strconv.ParseInt(c.Query("company_id"), 10, 64)
|
||||
if err != nil {
|
||||
return domain.ReportFilter{}, fmt.Errorf("invalid company_id: %w", err)
|
||||
}
|
||||
filter.CompanyID = domain.ValidInt64{Value: companyID, Valid: true}
|
||||
} else {
|
||||
filter.CompanyID = c.Locals("company_id").(domain.ValidInt64)
|
||||
|
||||
}
|
||||
|
||||
if c.Query("branch_id") != "" {
|
||||
if c.Query("branch_id") != "" && role == domain.RoleSuperAdmin {
|
||||
branchID, err := strconv.ParseInt(c.Query("branch_id"), 10, 64)
|
||||
if err != nil {
|
||||
return domain.ReportFilter{}, fmt.Errorf("invalid branch_id: %w", err)
|
||||
}
|
||||
filter.BranchID = domain.ValidInt64{Value: branchID, Valid: true}
|
||||
} else {
|
||||
filter.BranchID = c.Locals("branch_id").(domain.ValidInt64)
|
||||
}
|
||||
|
||||
if c.Query("user_id") != "" {
|
||||
|
|
|
|||
|
|
@ -116,6 +116,89 @@ func (h *Handler) GetShopBetByBetID(c *fiber.Ctx) error {
|
|||
return response.WriteJSON(c, fiber.StatusOK, "Shop bet fetched successfully", res, nil)
|
||||
}
|
||||
|
||||
// GetAllShopBets godoc
|
||||
// @Summary Gets all shop bets
|
||||
// @Description Gets all the shop bets
|
||||
// @Tags bet
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} domain.ShopBetRes
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /api/v1/shop/bet [get]
|
||||
func (h *Handler) GetAllShopBets(c *fiber.Ctx) error {
|
||||
// role := c.Locals("role").(domain.Role)
|
||||
companyID := c.Locals("company_id").(domain.ValidInt64)
|
||||
branchID := c.Locals("branch_id").(domain.ValidInt64)
|
||||
|
||||
searchQuery := c.Query("query")
|
||||
searchString := domain.ValidString{
|
||||
Value: searchQuery,
|
||||
Valid: searchQuery != "",
|
||||
}
|
||||
|
||||
createdBeforeQuery := c.Query("created_before")
|
||||
var createdBefore domain.ValidTime
|
||||
if createdBeforeQuery != "" {
|
||||
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
|
||||
if err != nil {
|
||||
h.mongoLoggerSvc.Info("invalid created_before format",
|
||||
zap.String("time", createdBeforeQuery),
|
||||
zap.Int("status_code", fiber.StatusBadRequest),
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
)
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
|
||||
}
|
||||
createdBefore = domain.ValidTime{
|
||||
Value: createdBeforeParsed,
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
createdAfterQuery := c.Query("created_after")
|
||||
var createdAfter domain.ValidTime
|
||||
if createdAfterQuery != "" {
|
||||
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
|
||||
if err != nil {
|
||||
h.mongoLoggerSvc.Info("invalid created_after format",
|
||||
zap.String("created_after", createdAfterQuery),
|
||||
zap.Int("status_code", fiber.StatusBadRequest),
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
)
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
|
||||
}
|
||||
createdAfter = domain.ValidTime{
|
||||
Value: createdAfterParsed,
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
bets, err := h.transactionSvc.GetAllShopBet(c.Context(), domain.ShopBetFilter{
|
||||
Query: searchString,
|
||||
CreatedBefore: createdBefore,
|
||||
CreatedAfter: createdAfter,
|
||||
CompanyID: companyID,
|
||||
BranchID: branchID,
|
||||
})
|
||||
if err != nil {
|
||||
h.mongoLoggerSvc.Error("Failed to get all bets",
|
||||
zap.Int("status_code", fiber.StatusInternalServerError),
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets፡"+err.Error())
|
||||
}
|
||||
|
||||
res := make([]domain.ShopBetRes, len(bets))
|
||||
for i, bet := range bets {
|
||||
res[i] = domain.ConvertShopBetDetail(bet)
|
||||
}
|
||||
|
||||
return response.WriteJSON(c, fiber.StatusOK, "All bets retrieved successfully", res, nil)
|
||||
}
|
||||
|
||||
// CashoutBet godoc
|
||||
// @Summary Cashout bet at branch
|
||||
// @Description Cashout bet at branch
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
|
|||
// TODO: Remove later
|
||||
_, err = h.walletSvc.AddToWallet(
|
||||
c.Context(), newWallet.RegularID, domain.ToCurrency(10000.0), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
|
||||
"Added 100.0 to wallet only as test for deployment")
|
||||
"Added 10000.0 to wallet only as test for deployment")
|
||||
|
||||
if err != nil {
|
||||
h.mongoLoggerSvc.Error("Failed to update wallet for user",
|
||||
|
|
@ -417,20 +417,121 @@ type UserProfileRes struct {
|
|||
LastLogin time.Time `json:"last_login"`
|
||||
SuspendedAt time.Time `json:"suspended_at"`
|
||||
Suspended bool `json:"suspended"`
|
||||
ReferralCode string `json:"referral_code"`
|
||||
}
|
||||
|
||||
// UserProfile godoc
|
||||
type CustomerProfileRes struct {
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Email string `json:"email"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
Role domain.Role `json:"role"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
LastLogin time.Time `json:"last_login"`
|
||||
SuspendedAt time.Time `json:"suspended_at"`
|
||||
Suspended bool `json:"suspended"`
|
||||
ReferralCode string `json:"referral_code"`
|
||||
}
|
||||
|
||||
// CustomerProfile godoc
|
||||
// @Summary Get user profile
|
||||
// @Description Get user profile
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} UserProfileRes
|
||||
// @Success 200 {object} CustomerProfileRes
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Security Bearer
|
||||
// @Router /api/v1/user/profile [get]
|
||||
func (h *Handler) UserProfile(c *fiber.Ctx) error {
|
||||
// @Router /api/v1/user/customer-profile [get]
|
||||
func (h *Handler) CustomerProfile(c *fiber.Ctx) error {
|
||||
|
||||
userID, ok := c.Locals("user_id").(int64)
|
||||
if !ok || userID == 0 {
|
||||
h.mongoLoggerSvc.Error("Invalid user ID in context",
|
||||
zap.Int64("userID", userID),
|
||||
zap.Int("status_code", fiber.StatusInternalServerError),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification")
|
||||
}
|
||||
|
||||
user, err := h.userSvc.GetUserByID(c.Context(), userID)
|
||||
if err != nil {
|
||||
h.mongoLoggerSvc.Error("Failed to get user profile",
|
||||
zap.Int64("userID", userID),
|
||||
zap.Int("status_code", fiber.StatusInternalServerError),
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
|
||||
}
|
||||
|
||||
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
|
||||
if err != nil {
|
||||
if err != authentication.ErrRefreshTokenNotFound {
|
||||
h.mongoLoggerSvc.Error("Failed to get user last login",
|
||||
zap.Int64("userID", userID),
|
||||
zap.Int("status_code", fiber.StatusInternalServerError),
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error())
|
||||
}
|
||||
|
||||
lastLogin = &user.CreatedAt
|
||||
}
|
||||
res := CustomerProfileRes{
|
||||
ID: user.ID,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Email: user.Email,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
Role: user.Role,
|
||||
EmailVerified: user.EmailVerified,
|
||||
PhoneVerified: user.PhoneVerified,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
SuspendedAt: user.SuspendedAt,
|
||||
Suspended: user.Suspended,
|
||||
LastLogin: *lastLogin,
|
||||
|
||||
}
|
||||
return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil)
|
||||
}
|
||||
|
||||
type AdminProfileRes struct {
|
||||
ID int64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Email string `json:"email"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
Role domain.Role `json:"role"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
PhoneVerified bool `json:"phone_verified"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
LastLogin time.Time `json:"last_login"`
|
||||
SuspendedAt time.Time `json:"suspended_at"`
|
||||
Suspended bool `json:"suspended"`
|
||||
}
|
||||
|
||||
// AdminProfile godoc
|
||||
// @Summary Get user profile
|
||||
// @Description Get user profile
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} AdminProfileRes
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Security Bearer
|
||||
// @Router /api/v1/user/admin-profile [get]
|
||||
func (h *Handler) AdminProfile(c *fiber.Ctx) error {
|
||||
|
||||
userID, ok := c.Locals("user_id").(int64)
|
||||
if !ok || userID == 0 {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ func (a *App) initAppRoutes() {
|
|||
groupV1.Post("/user/register", h.RegisterUser)
|
||||
groupV1.Post("/user/sendRegisterCode", h.SendRegisterCode)
|
||||
groupV1.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist)
|
||||
groupV1.Get("/user/profile", a.authMiddleware, h.UserProfile)
|
||||
groupV1.Get("/user/customer-profile", a.authMiddleware, h.CustomerProfile)
|
||||
groupV1.Get("/user/admin-profile", a.authMiddleware, h.AdminProfile)
|
||||
groupV1.Get("/user/single/:id", a.authMiddleware, h.GetUserByID)
|
||||
groupV1.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser)
|
||||
groupV1.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend)
|
||||
|
|
@ -174,6 +175,7 @@ func (a *App) initAppRoutes() {
|
|||
groupV1.Delete("/branch/:id", a.authMiddleware, h.DeleteBranch)
|
||||
|
||||
groupV1.Get("/search/branch", a.authMiddleware, h.SearchBranch)
|
||||
|
||||
groupV1.Get("/branchLocation", a.authMiddleware, h.GetAllBranchLocations)
|
||||
|
||||
groupV1.Get("/branch/:id/cashiers", a.authMiddleware, h.GetBranchCashiers)
|
||||
|
|
@ -287,6 +289,7 @@ func (a *App) initAppRoutes() {
|
|||
|
||||
// Transactions /shop/transactions
|
||||
groupV1.Post("/shop/bet", a.authMiddleware, a.CompanyOnly, h.CreateShopBet)
|
||||
groupV1.Get("/shop/bet", a.authMiddleware, a.CompanyOnly, h.GetAllShopBets)
|
||||
groupV1.Get("/shop/bet/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByBetID)
|
||||
groupV1.Post("/shop/bet/:id/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutBet)
|
||||
groupV1.Post("/shop/bet/:id/generate", a.authMiddleware, a.CompanyOnly, h.CashoutBet)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user