fix: show company wallet

This commit is contained in:
Samuel Tariku 2025-04-28 20:21:53 +03:00
parent 208a2d74be
commit 69c571ac5a
19 changed files with 200 additions and 101 deletions

View File

@ -249,6 +249,12 @@ CREATE TABLE companies (
admin_id BIGINT NOT NULL,
wallet_id BIGINT NOT NULL
);
CREATE VIEW companies_with_wallets AS
SELECT companies.*,
wallets.balance,
wallets.is_active
FROM companies
JOIN wallets ON wallets.id = companies.wallet_id;
ALTER TABLE refresh_tokens
ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE bets
@ -277,6 +283,9 @@ ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERE
ALTER TABLE branch_cashiers
ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(id),
ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches(id);
ALTER TABLE companies
ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id),
ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id);
----------------------------------------------seed data-------------------------------------------------------------
-------------------------------------- DO NOT USE IN PRODUCTION-------------------------------------------------
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@ -6,6 +6,7 @@ WHERE email = $1
-- name: CreateRefreshToken :exec
INSERT INTO refresh_tokens (user_id, token, expires_at, created_at, revoked)
VALUES ($1, $2, $3, $4, $5);
-- name: GetRefreshToken :one
SELECT *
FROM refresh_tokens

View File

@ -8,14 +8,14 @@ VALUES ($1, $2, $3)
RETURNING *;
-- name: GetAllCompanies :many
SELECT *
FROM companies;
FROM companies_with_wallets;
-- name: GetCompanyByID :one
SELECT *
FROM companies
FROM companies_with_wallets
WHERE id = $1;
-- name: SearchCompanyByName :many
SELECT *
FROM companies
FROM companies_with_wallets
WHERE name ILIKE '%' || $1 || '%';
-- name: UpdateCompany :one
UPDATE companies

View File

@ -50,24 +50,26 @@ func (q *Queries) DeleteCompany(ctx context.Context, id int64) error {
}
const GetAllCompanies = `-- name: GetAllCompanies :many
SELECT id, name, admin_id, wallet_id
FROM companies
SELECT id, name, admin_id, wallet_id, balance, is_active
FROM companies_with_wallets
`
func (q *Queries) GetAllCompanies(ctx context.Context) ([]Company, error) {
func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesWithWallet, error) {
rows, err := q.db.Query(ctx, GetAllCompanies)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Company
var items []CompaniesWithWallet
for rows.Next() {
var i Company
var i CompaniesWithWallet
if err := rows.Scan(
&i.ID,
&i.Name,
&i.AdminID,
&i.WalletID,
&i.Balance,
&i.IsActive,
); err != nil {
return nil, err
}
@ -80,43 +82,47 @@ func (q *Queries) GetAllCompanies(ctx context.Context) ([]Company, error) {
}
const GetCompanyByID = `-- name: GetCompanyByID :one
SELECT id, name, admin_id, wallet_id
FROM companies
SELECT id, name, admin_id, wallet_id, balance, is_active
FROM companies_with_wallets
WHERE id = $1
`
func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (Company, error) {
func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesWithWallet, error) {
row := q.db.QueryRow(ctx, GetCompanyByID, id)
var i Company
var i CompaniesWithWallet
err := row.Scan(
&i.ID,
&i.Name,
&i.AdminID,
&i.WalletID,
&i.Balance,
&i.IsActive,
)
return i, err
}
const SearchCompanyByName = `-- name: SearchCompanyByName :many
SELECT id, name, admin_id, wallet_id
FROM companies
SELECT id, name, admin_id, wallet_id, balance, is_active
FROM companies_with_wallets
WHERE name ILIKE '%' || $1 || '%'
`
func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text) ([]Company, error) {
func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text) ([]CompaniesWithWallet, error) {
rows, err := q.db.Query(ctx, SearchCompanyByName, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Company
var items []CompaniesWithWallet
for rows.Next() {
var i Company
var i CompaniesWithWallet
if err := rows.Scan(
&i.ID,
&i.Name,
&i.AdminID,
&i.WalletID,
&i.Balance,
&i.IsActive,
); err != nil {
return nil, err
}

View File

@ -145,6 +145,15 @@ type BranchOperation struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type CompaniesWithWallet struct {
ID int64 `json:"id"`
Name string `json:"name"`
AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"`
Balance int64 `json:"balance"`
IsActive bool `json:"is_active"`
}
type Company struct {
ID int64 `json:"id"`
Name string `json:"name"`

View File

@ -23,8 +23,8 @@ func ToCurrency(f float32) Currency {
return Currency((f * 100) + 0.5)
}
// Float64 converts a Currency to float32
func (m Currency) Float64() float32 {
// Float32 converts a Currency to float32
func (m Currency) Float32() float32 {
x := float32(m)
x = x / 100
return x

View File

@ -9,6 +9,16 @@ type Company struct {
AdminID int64
WalletID int64
}
type GetCompany struct {
ID int64
Name string
AdminID int64
WalletID int64
WalletBalance Currency
IsWalletActive bool
}
type CreateCompany struct {
Name string
AdminID int64

View File

@ -25,6 +25,17 @@ func convertDBCompany(dbCompany dbgen.Company) domain.Company {
}
}
func convertDBCompanyWithWallet(dbCompany dbgen.CompaniesWithWallet) domain.GetCompany {
return domain.GetCompany{
ID: dbCompany.ID,
Name: dbCompany.Name,
AdminID: dbCompany.AdminID,
WalletID: dbCompany.WalletID,
WalletBalance: domain.Currency(dbCompany.Balance),
IsWalletActive: dbCompany.IsActive,
}
}
func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) {
dbCompany, err := s.queries.CreateCompany(ctx, convertCreateCompany(company))
if err != nil {
@ -33,21 +44,21 @@ func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany)
return convertDBCompany(dbCompany), nil
}
func (s *Store) GetAllCompanies(ctx context.Context) ([]domain.Company, error) {
func (s *Store) GetAllCompanies(ctx context.Context) ([]domain.GetCompany, error) {
dbCompanies, err := s.queries.GetAllCompanies(ctx)
if err != nil {
return nil, err
}
var companies []domain.Company = make([]domain.Company, 0, len(dbCompanies))
var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies))
for _, dbCompany := range dbCompanies {
companies = append(companies, convertDBCompany(dbCompany))
companies = append(companies, convertDBCompanyWithWallet(dbCompany))
}
return companies, nil
}
func (s *Store) SearchCompanyByName(ctx context.Context, name string) ([]domain.Company, error) {
func (s *Store) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) {
dbCompanies, err := s.queries.SearchCompanyByName(ctx, pgtype.Text{
String: name,
Valid: true,
@ -56,21 +67,21 @@ func (s *Store) SearchCompanyByName(ctx context.Context, name string) ([]domain.
if err != nil {
return nil, err
}
var companies []domain.Company = make([]domain.Company, 0, len(dbCompanies))
var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies))
for _, dbCompany := range dbCompanies {
companies = append(companies, convertDBCompany(dbCompany))
companies = append(companies, convertDBCompanyWithWallet(dbCompany))
}
return companies, nil
}
func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.Company, error) {
func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) {
dbCompany, err := s.queries.GetCompanyByID(ctx, id)
if err != nil {
return domain.Company{}, err
return domain.GetCompany{}, err
}
return convertDBCompany(dbCompany), nil
return convertDBCompanyWithWallet(dbCompany), nil
}
func (s *Store) UpdateCompany(ctx context.Context, id int64, company domain.UpdateCompany) (domain.Company, error) {

View File

@ -39,6 +39,20 @@ func (s *Service) Login(ctx context.Context, email, phone string, password strin
return LoginSuccess{}, ErrUserSuspended
}
oldRefreshToken, err := s.tokenStore.GetRefreshTokenByUserID(ctx, user.ID)
if err != nil && err != ErrRefreshTokenNotFound {
return LoginSuccess{}, err
}
// If old refresh token is not revoked, revoke it
if err == nil && !oldRefreshToken.Revoked {
err = s.tokenStore.RevokeRefreshToken(ctx, oldRefreshToken.Token)
if(err != nil) {
return LoginSuccess{}, err
}
}
refreshToken, err := generateRefreshToken()
if err != nil {
return LoginSuccess{}, err
@ -49,6 +63,7 @@ func (s *Service) Login(ctx context.Context, email, phone string, password strin
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(time.Duration(s.RefreshExpiry) * time.Second),
})
if err != nil {
return LoginSuccess{}, err
}

View File

@ -8,9 +8,9 @@ import (
type CompanyStore interface {
CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error)
GetAllCompanies(ctx context.Context) ([]domain.Company, error)
GetCompanyByID(ctx context.Context, id int64) (domain.Company, error)
SearchCompanyByName(ctx context.Context, name string) ([]domain.Company, error)
GetAllCompanies(ctx context.Context) ([]domain.GetCompany, error)
SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error)
GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error)
UpdateCompany(ctx context.Context, id int64, company domain.UpdateCompany) (domain.Company, error)
DeleteCompany(ctx context.Context, id int64) error
}

View File

@ -19,15 +19,15 @@ func NewService(companyStore CompanyStore) *Service {
func (s *Service) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) {
return s.companyStore.CreateCompany(ctx, company)
}
func (s *Service) GetAllCompanies(ctx context.Context) ([]domain.Company, error) {
func (s *Service) GetAllCompanies(ctx context.Context) ([]domain.GetCompany, error) {
return s.companyStore.GetAllCompanies(ctx)
}
func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.Company, error) {
func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) {
return s.companyStore.GetCompanyByID(ctx, id)
}
func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.Company, error) {
func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) {
return s.companyStore.SearchCompanyByName(ctx, name)
}

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strconv"
"sync"
@ -98,63 +99,78 @@ func (s *service) FetchLiveEvents(ctx context.Context) error {
func (s *service) FetchUpcomingEvents(ctx context.Context) error {
sportIDs := []int{1}
var totalPages int = 1
var page int = 0
// var limit int = 5
// var count int = 0
for _, sportID := range sportIDs {
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s", sportID, s.token)
resp, err := http.Get(url)
if err != nil {
continue
}
defer resp.Body.Close()
for page != totalPages {
page = page + 1
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s&page=%d", sportID, s.token, page)
log.Printf("📡 Fetching data for event data page %d", page)
resp, err := http.Get(url)
if err != nil {
log.Printf("❌ Failed to fetch event data for page %d: %v", page, err)
continue
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data struct {
Success int `json:"success"`
Results []struct {
ID string `json:"id"`
SportID string `json:"sport_id"`
Time string `json:"time"`
League struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"league"`
Home struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"home"`
Away *struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"away"`
} `json:"results"`
}
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
continue
}
for _, ev := range data.Results {
startUnix, _ := strconv.ParseInt(ev.Time, 10, 64)
event := domain.UpcomingEvent{
ID: ev.ID,
SportID: ev.SportID,
MatchName: ev.Home.Name,
HomeTeam: ev.Home.Name,
AwayTeam: "", // handle nil safely
HomeTeamID: ev.Home.ID,
AwayTeamID: "",
HomeKitImage: "",
AwayKitImage: "",
LeagueID: ev.League.ID,
LeagueName: ev.League.Name,
LeagueCC: "",
StartTime: time.Unix(startUnix, 0).UTC(),
body, _ := io.ReadAll(resp.Body)
var data struct {
Success int `json:"success"`
Pager struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
}
Results []struct {
ID string `json:"id"`
SportID string `json:"sport_id"`
Time string `json:"time"`
League struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"league"`
Home struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"home"`
Away *struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"away"`
} `json:"results"`
}
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
continue
}
if ev.Away != nil {
event.AwayTeam = ev.Away.Name
event.AwayTeamID = ev.Away.ID
}
for _, ev := range data.Results {
startUnix, _ := strconv.ParseInt(ev.Time, 10, 64)
event := domain.UpcomingEvent{
ID: ev.ID,
SportID: ev.SportID,
MatchName: ev.Home.Name,
HomeTeam: ev.Home.Name,
AwayTeam: "", // handle nil safely
HomeTeamID: ev.Home.ID,
AwayTeamID: "",
HomeKitImage: "",
AwayKitImage: "",
LeagueID: ev.League.ID,
LeagueName: ev.League.Name,
LeagueCC: "",
StartTime: time.Unix(startUnix, 0).UTC(),
}
_ = s.store.SaveUpcomingEvent(ctx, event)
if ev.Away != nil {
event.AwayTeam = ev.Away.Name
event.AwayTeamID = ev.Away.ID
}
_ = s.store.SaveUpcomingEvent(ctx, event)
}
totalPages = data.Pager.Total
}
}

View File

@ -94,6 +94,7 @@ func (h *Handler) RefreshToken(c *fiber.Ctx) error {
type loginCustomerRes struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
Role string `json:"role"`
}
var req refreshToken
@ -132,6 +133,7 @@ func (h *Handler) RefreshToken(c *fiber.Ctx) error {
res := loginCustomerRes{
AccessToken: accessToken,
RefreshToken: req.RefreshToken,
Role: string(user.Role),
}
return response.WriteJSON(c, fiber.StatusOK, "Refresh successful", res, nil)
}

View File

@ -55,7 +55,7 @@ type BetRes struct {
func convertCreateBet(bet domain.Bet, createdNumber int64) CreateBetRes {
return CreateBetRes{
ID: bet.ID,
Amount: bet.Amount.Float64(),
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
FullName: bet.FullName,
@ -70,7 +70,7 @@ func convertCreateBet(bet domain.Bet, createdNumber int64) CreateBetRes {
func convertBet(bet domain.GetBet) BetRes {
return BetRes{
ID: bet.ID,
Amount: bet.Amount.Float64(),
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
FullName: bet.FullName,

View File

@ -20,6 +20,15 @@ type CompanyRes struct {
WalletID int64 `json:"wallet_id" example:"1"`
}
type GetCompanyRes struct {
ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"CompanyName"`
AdminID int64 `json:"admin_id" example:"1"`
WalletID int64 `json:"wallet_id" example:"1"`
WalletBalance float32 `json:"balance" example:"1"`
IsActive bool `json:"is_active" example:"false"`
}
func convertCompany(company domain.Company) CompanyRes {
return CompanyRes{
ID: company.ID,
@ -29,6 +38,17 @@ func convertCompany(company domain.Company) CompanyRes {
}
}
func convertGetCompany(company domain.GetCompany) GetCompanyRes {
return GetCompanyRes{
ID: company.ID,
Name: company.Name,
AdminID: company.AdminID,
WalletID: company.WalletID,
WalletBalance: company.WalletBalance.Float32(),
IsActive: company.IsWalletActive,
}
}
// CreateCompany godoc
// @Summary Create a company
// @Description Creates a company
@ -100,10 +120,10 @@ func (h *Handler) GetAllCompanies(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get companies", err, nil)
}
var result []CompanyRes = make([]CompanyRes, 0, len(companies))
var result []GetCompanyRes = make([]GetCompanyRes, 0, len(companies))
for _, company := range companies {
result = append(result, convertCompany(company))
result = append(result, convertGetCompany(company))
}
return response.WriteJSON(c, fiber.StatusOK, "All Companies retrieved", result, nil)
@ -137,7 +157,7 @@ func (h *Handler) GetCompanyByID(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to company branch", err, nil)
}
res := convertCompany(company)
res := convertGetCompany(company)
return response.WriteJSON(c, fiber.StatusOK, "Company retrieved successfully", res, nil)
@ -164,10 +184,10 @@ func (h *Handler) SearchCompany(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get companies", err, nil)
}
var result []CompanyRes = make([]CompanyRes, 0, len(companies))
var result []GetCompanyRes = make([]GetCompanyRes, 0, len(companies))
for _, company := range companies {
result = append(result, convertCompany(company))
result = append(result, convertGetCompany(company))
}
return response.WriteJSON(c, fiber.StatusOK, "All Companies retrieved", result, nil)

View File

@ -190,7 +190,7 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
res := TicketRes{
ID: ticket.ID,
Outcomes: ticket.Outcomes,
Amount: ticket.Amount.Float64(),
Amount: ticket.Amount.Float32(),
TotalOdds: ticket.TotalOdds,
}
return response.WriteJSON(c, fiber.StatusOK, "Ticket retrieved successfully", res, nil)
@ -219,7 +219,7 @@ func (h *Handler) GetAllTickets(c *fiber.Ctx) error {
res[i] = TicketRes{
ID: ticket.ID,
Outcomes: ticket.Outcomes,
Amount: ticket.Amount.Float64(),
Amount: ticket.Amount.Float32(),
TotalOdds: ticket.TotalOdds,
}
}

View File

@ -45,7 +45,7 @@ type CreateTransactionReq struct {
func convertTransaction(transaction domain.Transaction) TransactionRes {
return TransactionRes{
ID: transaction.ID,
Amount: transaction.Amount.Float64(),
Amount: transaction.Amount.Float32(),
BranchID: transaction.BranchID,
CashierID: transaction.CashierID,
BetID: transaction.BetID,

View File

@ -47,7 +47,7 @@ func convertTransfer(transfer domain.Transfer) TransferWalletRes {
return TransferWalletRes{
ID: transfer.ID,
Amount: transfer.Amount.Float64(),
Amount: transfer.Amount.Float32(),
Verified: transfer.Verified,
Type: string(transfer.Type),
PaymentMethod: string(transfer.PaymentMethod),

View File

@ -24,7 +24,7 @@ type WalletRes struct {
func convertWallet(wallet domain.Wallet) WalletRes {
return WalletRes{
ID: wallet.ID,
Balance: wallet.Balance.Float64(),
Balance: wallet.Balance.Float32(),
IsWithdraw: wallet.IsWithdraw,
IsBettable: wallet.IsBettable,
IsTransferable: wallet.IsTransferable,
@ -52,9 +52,9 @@ func convertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes {
return CustomerWalletRes{
ID: wallet.ID,
RegularID: wallet.RegularID,
RegularBalance: wallet.RegularBalance.Float64(),
RegularBalance: wallet.RegularBalance.Float32(),
StaticID: wallet.StaticID,
StaticBalance: wallet.StaticBalance.Float64(),
StaticBalance: wallet.StaticBalance.Float32(),
CustomerID: wallet.CustomerID,
CompanyID: wallet.CompanyID,
RegularUpdatedAt: wallet.RegularUpdatedAt,
@ -159,7 +159,7 @@ func (h *Handler) GetAllBranchWallets(c *fiber.Ctx) error {
for _, wallet := range wallets {
res = append(res, BranchWalletRes{
ID: wallet.ID,
Balance: wallet.Balance.Float64(),
Balance: wallet.Balance.Float32(),
IsActive: wallet.IsActive,
Name: wallet.Name,
Location: wallet.Location,