additions and small fixes

This commit is contained in:
Samuel Tariku 2025-04-09 15:33:51 +03:00
parent c9df0b3303
commit 7dd6221f74
29 changed files with 682 additions and 100 deletions

View File

@ -64,13 +64,15 @@ CREATE TABLE IF NOT EXISTS tickets (
-- CREATE TABLE IF NOT EXISTS bet_outcomes (
-- id BIGSERIAL PRIMARY KEY,
-- bet_id BIGINT NOT NULL,
-- outcome_id BIGINT NOT NULL,
-- event_id bigint not null,
-- odd_id BIGINT NOT NULL,
-- );
-- CREATE TABLE IF NOT EXISTS ticket_outcomes (
-- id BIGSERIAL PRIMARY KEY,
-- ticket_id BIGINT NOT NULL,
-- outcome_id BIGINT NOT NULL,
-- event_id bigint not null,
-- odd_id BIGINT NOT NULL,
-- );
CREATE TABLE IF NOT EXISTS wallets (
@ -186,6 +188,25 @@ INSERT INTO users (
FALSE
);
INSERT INTO users (
first_name, last_name, email, phone_number, password, role,
email_verified, phone_verified, created_at, updated_at,
suspended_at, suspended
) VALUES (
'Samuel',
'Tariku',
'cybersamt@gmail.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'cashier',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
NULL,
FALSE
);
INSERT INTO supported_operations (
name, description

View File

@ -20,6 +20,9 @@ SELECT * FROM branch_details WHERE company_id = $1;
-- name: GetBranchByManagerID :many
SELECT * FROM branch_details WHERE branch_manager_id = $1;
-- name: SearchBranchByName :many
SELECT * FROM branch_details WHERE name ILIKE '%' || $1 || '%';
-- name: GetAllSupportedOperations :many
SELECT * FROM supported_operations;

View File

@ -13,6 +13,13 @@ WHERE id = $1;
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
FROM users;
-- name: SearchUserByNameOrPhone :many
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
FROM users
WHERE first_name ILIKE '%' || $1 || '%'
OR last_name ILIKE '%' || $1 || '%'
OR phone_number LIKE '%' || $1 || '%';
-- name: UpdateUser :exec
UPDATE users
SET first_name = $1, last_name = $2, email = $3, phone_number = $4, role = $5, updated_at = $6

View File

@ -857,6 +857,42 @@ const docTemplate = `{
}
},
"/supportedOperation": {
"get": {
"description": "Gets all supported operations",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets all supported operations",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Creates a supported operation",
"consumes": [
@ -1429,6 +1465,52 @@ const docTemplate = `{
}
}
},
"/user/search": {
"post": {
"description": "Search for user using name or phone",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Search for user using name or phone",
"parameters": [
{
"description": "Search for using his name or phone",
"name": "searchUserByNameOrPhone",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.SearchUserByNameOrPhoneReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.UserProfileRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/sendRegisterCode": {
"post": {
"description": "Send register code",
@ -1983,6 +2065,12 @@ const docTemplate = `{
"name": {
"type": "string",
"example": "4-kilo Branch"
},
"operations": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
@ -2081,7 +2169,21 @@ const docTemplate = `{
}
},
"handlers.CreateTransferReq": {
"type": "object"
"type": "object",
"properties": {
"amount": {
"type": "number",
"example": 100
},
"payment_method": {
"type": "string",
"example": "cash"
},
"receiver_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CustomerWalletRes": {
"type": "object",
@ -2202,6 +2304,14 @@ const docTemplate = `{
}
}
},
"handlers.SearchUserByNameOrPhoneReq": {
"type": "object",
"properties": {
"searchString": {
"type": "string"
}
}
},
"handlers.SupportedOperationRes": {
"type": "object",
"properties": {

View File

@ -849,6 +849,42 @@
}
},
"/supportedOperation": {
"get": {
"description": "Gets all supported operations",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets all supported operations",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.BranchDetailRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Creates a supported operation",
"consumes": [
@ -1421,6 +1457,52 @@
}
}
},
"/user/search": {
"post": {
"description": "Search for user using name or phone",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Search for user using name or phone",
"parameters": [
{
"description": "Search for using his name or phone",
"name": "searchUserByNameOrPhone",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.SearchUserByNameOrPhoneReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.UserProfileRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/sendRegisterCode": {
"post": {
"description": "Send register code",
@ -1975,6 +2057,12 @@
"name": {
"type": "string",
"example": "4-kilo Branch"
},
"operations": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
@ -2073,7 +2161,21 @@
}
},
"handlers.CreateTransferReq": {
"type": "object"
"type": "object",
"properties": {
"amount": {
"type": "number",
"example": 100
},
"payment_method": {
"type": "string",
"example": "cash"
},
"receiver_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CustomerWalletRes": {
"type": "object",
@ -2194,6 +2296,14 @@
}
}
},
"handlers.SearchUserByNameOrPhoneReq": {
"type": "object",
"properties": {
"searchString": {
"type": "string"
}
}
},
"handlers.SupportedOperationRes": {
"type": "object",
"properties": {

View File

@ -199,6 +199,10 @@ definitions:
name:
example: 4-kilo Branch
type: string
operations:
items:
type: integer
type: array
type: object
handlers.CreateSupportedOperationReq:
properties:
@ -265,6 +269,16 @@ definitions:
type: string
type: object
handlers.CreateTransferReq:
properties:
amount:
example: 100
type: number
payment_method:
example: cash
type: string
receiver_id:
example: 1
type: integer
type: object
handlers.CustomerWalletRes:
properties:
@ -350,6 +364,11 @@ definitions:
phoneNumber:
type: string
type: object
handlers.SearchUserByNameOrPhoneReq:
properties:
searchString:
type: string
type: object
handlers.SupportedOperationRes:
properties:
description:
@ -1135,6 +1154,30 @@ paths:
tags:
- branch
/supportedOperation:
get:
consumes:
- application/json
description: Gets all supported operations
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.BranchDetailRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets all supported operations
tags:
- branch
post:
consumes:
- application/json
@ -1509,6 +1552,36 @@ paths:
summary: Reset password
tags:
- user
/user/search:
post:
consumes:
- application/json
description: Search for user using name or phone
parameters:
- description: Search for using his name or phone
in: body
name: searchUserByNameOrPhone
required: true
schema:
$ref: '#/definitions/handlers.SearchUserByNameOrPhoneReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.UserProfileRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Search for user using name or phone
tags:
- user
/user/sendRegisterCode:
post:
consumes:

View File

@ -309,6 +309,42 @@ func (q *Queries) GetBranchOperations(ctx context.Context, branchID int64) ([]Ge
return items, nil
}
const SearchBranchByName = `-- name: SearchBranchByName :many
SELECT id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number FROM branch_details WHERE name ILIKE '%' || $1 || '%'
`
func (q *Queries) SearchBranchByName(ctx context.Context, dollar_1 pgtype.Text) ([]BranchDetail, error) {
rows, err := q.db.Query(ctx, SearchBranchByName, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
var items []BranchDetail
for rows.Next() {
var i BranchDetail
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Location,
&i.WalletID,
&i.BranchManagerID,
&i.CompanyID,
&i.IsSelfOwned,
&i.CreatedAt,
&i.UpdatedAt,
&i.ManagerName,
&i.ManagerPhoneNumber,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateBranch = `-- name: UpdateBranch :one
UPDATE branches SET name = $1, location = $2, branch_manager_id = $3, company_id = $4, is_self_owned = $5 WHERE id = $6 RETURNING id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at
`

View File

@ -256,6 +256,58 @@ func (q *Queries) GetUserByPhone(ctx context.Context, phoneNumber pgtype.Text) (
return i, err
}
const SearchUserByNameOrPhone = `-- name: SearchUserByNameOrPhone :many
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
FROM users
WHERE first_name ILIKE '%' || $1 || '%'
OR last_name ILIKE '%' || $1 || '%'
OR phone_number LIKE '%' || $1 || '%'
`
type SearchUserByNameOrPhoneRow struct {
ID int64
FirstName string
LastName string
Email pgtype.Text
PhoneNumber pgtype.Text
Role string
EmailVerified bool
PhoneVerified bool
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
}
func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, dollar_1 pgtype.Text) ([]SearchUserByNameOrPhoneRow, error) {
rows, err := q.db.Query(ctx, SearchUserByNameOrPhone, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
var items []SearchUserByNameOrPhoneRow
for rows.Next() {
var i SearchUserByNameOrPhoneRow
if err := rows.Scan(
&i.ID,
&i.FirstName,
&i.LastName,
&i.Email,
&i.PhoneNumber,
&i.Role,
&i.EmailVerified,
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdatePassword = `-- name: UpdatePassword :exec
UPDATE users
SET password = $1, updated_at = $4

View File

@ -33,7 +33,7 @@ type RegisterUserReq struct {
Email string
PhoneNumber string
Password string
//Role string
Role string
Otp string
ReferalCode string
//

View File

@ -73,7 +73,7 @@ func (s *Store) GetAllBets(ctx context.Context) ([]domain.Bet, error) {
return nil, err
}
var result []domain.Bet = make([]domain.Bet, len(bets))
var result []domain.Bet = make([]domain.Bet, 0, len(bets))
for _, bet := range bets {
result = append(result, convertDBBet(bet))
}

View File

@ -5,6 +5,7 @@ import (
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
func convertCreateBranch(branch domain.CreateBranch) dbgen.CreateBranchParams {
@ -54,29 +55,6 @@ func (s *Store) CreateBranch(ctx context.Context, branch domain.CreateBranch) (d
return convertDBBranch(dbBranch), nil
}
func (s *Store) CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) {
dbSupportedOperation, err := s.queries.CreateSupportedOperation(ctx, dbgen.CreateSupportedOperationParams{
Name: supportedOperation.Name,
Description: supportedOperation.Description,
})
if err != nil {
return domain.SupportedOperation{}, err
}
return domain.SupportedOperation{
ID: dbSupportedOperation.ID,
Name: dbSupportedOperation.Name,
Description: dbSupportedOperation.Description,
}, nil
}
func (s *Store) CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error {
_, err := s.queries.CreateBranchOperation(ctx, dbgen.CreateBranchOperationParams{
BranchID: branchOperation.BranchID,
OperationID: branchOperation.OperationID,
})
return err
}
func (s *Store) GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) {
dbBranch, err := s.queries.GetBranchByID(ctx, id)
if err != nil {
@ -108,22 +86,6 @@ func (s *Store) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]do
return branches, nil
}
func (s *Store) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) {
dbBranchOperations, err := s.queries.GetBranchOperations(ctx, branchID)
if err != nil {
return nil, err
}
var branchOperations []domain.BranchOperation = make([]domain.BranchOperation, 0, len(dbBranchOperations))
for _, dbBranchOperation := range dbBranchOperations {
branchOperations = append(branchOperations, domain.BranchOperation{
ID: dbBranchOperation.ID,
OperationName: dbBranchOperation.Name,
OperationDescription: dbBranchOperation.Description,
})
}
return branchOperations, nil
}
func (s *Store) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.GetAllBranches(ctx)
if err != nil {
@ -136,6 +98,19 @@ func (s *Store) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, erro
return branches, nil
}
func (s *Store) SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.SearchBranchByName(ctx, pgtype.Text{String: name, Valid: true})
if err != nil {
return nil, err
}
var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
for _, dbBranch := range dbBranches {
branches = append(branches, convertDBBranchDetail(dbBranch))
}
return branches, nil
}
func (s *Store) UpdateBranch(ctx context.Context, id int64, branch domain.UpdateBranch) (domain.Branch, error) {
dbBranch, err := s.queries.UpdateBranch(ctx, dbgen.UpdateBranchParams{
ID: id,
@ -154,6 +129,65 @@ func (s *Store) DeleteBranch(ctx context.Context, id int64) error {
return s.queries.DeleteBranch(ctx, id)
}
// Branch Operations
func (s *Store) CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error {
_, err := s.queries.CreateBranchOperation(ctx, dbgen.CreateBranchOperationParams{
BranchID: branchOperation.BranchID,
OperationID: branchOperation.OperationID,
})
return err
}
func (s *Store) CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) {
dbSupportedOperation, err := s.queries.CreateSupportedOperation(ctx, dbgen.CreateSupportedOperationParams{
Name: supportedOperation.Name,
Description: supportedOperation.Description,
})
if err != nil {
return domain.SupportedOperation{}, err
}
return domain.SupportedOperation{
ID: dbSupportedOperation.ID,
Name: dbSupportedOperation.Name,
Description: dbSupportedOperation.Description,
}, nil
}
func (s *Store) GetAllSupportedOperations(ctx context.Context) ([]domain.SupportedOperation, error) {
dbOperations, err := s.queries.GetAllSupportedOperations(ctx)
if err != nil {
return nil, err
}
var operations []domain.SupportedOperation = make([]domain.SupportedOperation, 0, len(dbOperations))
for _, dbOperation := range dbOperations {
operations = append(operations, domain.SupportedOperation{
ID: dbOperation.ID,
Name: dbOperation.Name,
Description: dbOperation.Description,
})
}
return operations, nil
}
func (s *Store) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) {
dbBranchOperations, err := s.queries.GetBranchOperations(ctx, branchID)
if err != nil {
return nil, err
}
var branchOperations []domain.BranchOperation = make([]domain.BranchOperation, 0, len(dbBranchOperations))
for _, dbBranchOperation := range dbBranchOperations {
branchOperations = append(branchOperations, domain.BranchOperation{
ID: dbBranchOperation.ID,
OperationName: dbBranchOperation.Name,
OperationDescription: dbBranchOperation.Description,
})
}
return branchOperations, nil
}
func (s *Store) DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error {
err := s.queries.DeleteBranchOperation(ctx, dbgen.DeleteBranchOperationParams{
BranchID: branchID,

View File

@ -51,7 +51,7 @@ func (s *Store) GetAllTickets(ctx context.Context) ([]domain.Ticket, error) {
return nil, err
}
var result []domain.Ticket = make([]domain.Ticket, len(tickets))
var result []domain.Ticket = make([]domain.Ticket, 0, len(tickets))
for _, ticket := range tickets {
result = append(result, convertDBTicket(ticket))
}

View File

@ -66,7 +66,7 @@ func (s *Store) GetAllTransactions(ctx context.Context) ([]domain.Transaction, e
return nil, err
}
var result []domain.Transaction = make([]domain.Transaction, len(transaction))
var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBTransaction(ticket))
}
@ -79,7 +79,7 @@ func (s *Store) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.
return nil, err
}
var result []domain.Transaction = make([]domain.Transaction, len(transaction))
var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBTransaction(ticket))
}

View File

@ -57,7 +57,7 @@ func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error)
if err != nil {
return nil, err
}
var result []domain.Transfer = make([]domain.Transfer, len(transfers))
var result []domain.Transfer = make([]domain.Transfer, 0, len(transfers))
for _, transfer := range transfers {
result = append(result, convertDBTransfer(transfer))
@ -70,7 +70,7 @@ func (s *Store) GetTransfersByWallet(ctx context.Context, walletID int64) ([]dom
return nil, err
}
var result []domain.Transfer = make([]domain.Transfer, len(transfers))
var result []domain.Transfer = make([]domain.Transfer, 0, len(transfers))
for _, transfer := range transfers {
result = append(result, convertDBTransfer(transfer))

View File

@ -100,6 +100,30 @@ func (s *Store) GetAllUsers(ctx context.Context) ([]domain.User, error) {
}
return userList, nil
}
func (s *Store) SearchUserByNameOrPhone(ctx context.Context, searchString string) ([]domain.User, error) {
users, err := s.queries.SearchUserByNameOrPhone(ctx, pgtype.Text{
String: searchString,
Valid: true,
})
if err != nil {
return nil, err
}
userList := make([]domain.User, 0, len(users))
for _, user := range users {
userList = append(userList, domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
})
}
return userList, nil
}
func (s *Store) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error {
err := s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{
// ID: user.ID,

View File

@ -95,7 +95,7 @@ func (s *Store) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) {
return nil, err
}
var result []domain.Wallet = make([]domain.Wallet, len(wallets))
var result []domain.Wallet = make([]domain.Wallet, 0, len(wallets))
for _, wallet := range wallets {
result = append(result, convertDBWallet(wallet))
@ -109,7 +109,7 @@ func (s *Store) GetWalletsByUser(ctx context.Context, userID int64) ([]domain.Wa
return nil, err
}
var result []domain.Wallet = make([]domain.Wallet, len(wallets))
var result []domain.Wallet = make([]domain.Wallet, 0, len(wallets))
for _, wallet := range wallets {
result = append(result, convertDBWallet(wallet))

View File

@ -15,6 +15,8 @@ type BranchStore interface {
GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error)
GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error)
GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error)
GetAllSupportedOperations(ctx context.Context) ([]domain.SupportedOperation, error)
SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error)
UpdateBranch(ctx context.Context, id int64, branch domain.UpdateBranch) (domain.Branch, error)
DeleteBranch(ctx context.Context, id int64) error
DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error

View File

@ -40,6 +40,14 @@ func (s *Service) GetBranchOperations(ctx context.Context, branchID int64) ([]do
func (s *Service) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
return s.branchStore.GetAllBranches(ctx)
}
func (s *Service) GetAllSupportedOperations(ctx context.Context) ([]domain.SupportedOperation, error) {
return s.branchStore.GetAllSupportedOperations(ctx)
}
func (s *Service) SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) {
return s.branchStore.SearchBranchByName(ctx, name)
}
func (s *Service) UpdateBranch(ctx context.Context, id int64, branch domain.UpdateBranch) (domain.Branch, error) {
return s.branchStore.UpdateBranch(ctx, id, branch)
}

View File

@ -15,7 +15,7 @@ type UserStore interface {
CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error)
GetUserByEmail(ctx context.Context, email string) (domain.User, error)
GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error)
//
SearchUserByNameOrPhone(ctx context.Context, searchString string) ([]domain.User, error)
UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error // identifier verified email or phone
}
type SmsGateway interface {

View File

@ -65,7 +65,7 @@ func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterU
Email: registerReq.Email,
PhoneNumber: registerReq.PhoneNumber,
Password: hashedPassword,
Role: "user",
Role: domain.RoleCustomer,
EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail,
PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms,
}

View File

@ -6,6 +6,14 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func (s *Service) SearchUserByNameOrPhone(ctx context.Context, searchString string) ([]domain.User, error) {
// Search user
return s.userStore.SearchUserByNameOrPhone(ctx, searchString)
}
func (s *Service) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error {
// update user
return s.userStore.UpdateUser(ctx, user)

View File

@ -62,8 +62,10 @@ func convertBet(bet domain.Bet) BetRes {
func CreateBet(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
// TODO if user is customer, get id from the token then get the wallet id from there
// TODO: If user is a cashier, check the token, and find the role and get the branch id from there. Reduce amount from the branch wallet
var isShopBet bool = true
var branchID int64 = 1
var userID int64
@ -134,7 +136,7 @@ func GetAllBet(logger *slog.Logger, betSvc *bet.Service, validator *customvalida
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bets", err, nil)
}
var res []BetRes = make([]BetRes, len(bets))
var res []BetRes = make([]BetRes, 0, len(bets))
for _, bet := range bets {
res = append(res, convertBet(bet))
}

View File

@ -18,6 +18,7 @@ type CreateBranchReq struct {
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"`
IsSelfOwned bool `json:"is_self_owned" example:"false"`
Operations []int64 `json:"operations"`
}
type CreateSupportedOperationReq struct {
@ -310,7 +311,7 @@ func GetBranchByManagerID(logger *slog.Logger, branchSvc *branch.Service, valida
logger.Error("Failed to get branches", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
}
var result []BranchDetailRes = make([]BranchDetailRes, len(branches))
var result []BranchDetailRes = make([]BranchDetailRes, 0, len(branches))
for _, branch := range branches {
result = append(result, convertBranchDetail(branch))
}
@ -344,7 +345,7 @@ func GetBranchByCompanyID(logger *slog.Logger, branchSvc *branch.Service, valida
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
}
var result []BranchDetailRes = make([]BranchDetailRes, len(branches))
var result []BranchDetailRes = make([]BranchDetailRes, 0, len(branches))
for _, branch := range branches {
result = append(result, convertBranchDetail(branch))
}
@ -370,7 +371,7 @@ func GetAllBranches(logger *slog.Logger, branchSvc *branch.Service, validator *c
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
}
var result []BranchDetailRes = make([]BranchDetailRes, len(branches))
var result []BranchDetailRes = make([]BranchDetailRes, 0, len(branches))
for _, branch := range branches {
result = append(result, convertBranchDetail(branch))
}
@ -379,6 +380,37 @@ func GetAllBranches(logger *slog.Logger, branchSvc *branch.Service, validator *c
}
}
// GetAllSupportedOperations godoc
// @Summary Gets all supported operations
// @Description Gets all supported operations
// @Tags branch
// @Accept json
// @Produce json
// @Success 200 {array} BranchDetailRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /supportedOperation [get]
func GetAllSupportedOperations(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
operations, err := branchSvc.GetAllSupportedOperations(c.Context())
if err != nil {
logger.Error("Failed to get operations", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get operations", err, nil)
}
var result []SupportedOperationRes = make([]SupportedOperationRes, 0, len(operations))
for _, operation := range operations {
result = append(result, SupportedOperationRes{
ID: operation.ID,
Name: operation.Name,
Description: operation.Description,
})
}
return response.WriteJSON(c, fiber.StatusOK, "SupportedOperations for Company retrieved", result, nil)
}
}
// GetBranchOperations godoc
// @Summary Gets branch operations
// @Description Gets branch operations
@ -407,7 +439,7 @@ func GetBranchOperations(logger *slog.Logger, branchSvc *branch.Service, validat
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve operation", err, nil)
}
var result []BranchOperationRes = make([]BranchOperationRes, len(operations))
var result []BranchOperationRes = make([]BranchOperationRes, 0, len(operations))
for _, branch := range operations {
result = append(result, BranchOperationRes{

View File

@ -136,7 +136,7 @@ func GetAllTickets(logger *slog.Logger, ticketSvc *ticket.Service,
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve tickets", err, nil)
}
var res []TicketRes = make([]TicketRes, len(tickets))
var res []TicketRes = make([]TicketRes, 0, len(tickets))
for _, ticket := range tickets {
res = append(res, TicketRes{

View File

@ -167,7 +167,7 @@ func GetAllTransactions(
}
// Convert transactions to response format
var res []TransactionRes = make([]TransactionRes, len(transactions))
var res []TransactionRes = make([]TransactionRes, 0, len(transactions))
for i, transaction := range transactions {
res[i] = convertTransaction(transaction)
}

View File

@ -25,7 +25,6 @@ type TransferRes struct {
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
}
func convertTransfer(transfer domain.Transfer) TransferRes {
var senderWalletID *int64
if transfer.SenderWalletID.Valid {
@ -52,9 +51,9 @@ func convertTransfer(transfer domain.Transfer) TransferRes {
}
type CreateTransferReq struct {
receiverID int64
amount float64
paymentMethod string
ReceiverID int64 `json:"receiver_id" example:"1"`
Amount float64 `json:"amount" example:"100.0"`
PaymentMethod string `json:"payment_method" example:"cash"`
}
// TransferToWallet godoc
@ -101,7 +100,7 @@ func TransferToWallet(logger *slog.Logger, walletSvc *wallet.Service, branchSvc
return nil
}
transfer, err := walletSvc.TransferToWallet(c.Context(), branchWallet.ID, req.receiverID, domain.Currency(req.amount), domain.PaymentMethod(req.paymentMethod), domain.ValidInt64{Value: userID, Valid: true})
transfer, err := walletSvc.TransferToWallet(c.Context(), branchWallet.ID, req.ReceiverID, domain.Currency(req.Amount), domain.PaymentMethod(req.PaymentMethod), domain.ValidInt64{Value: userID, Valid: true})
if !ok {
response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil)

View File

@ -374,3 +374,61 @@ func getMedium(email, phoneNumber string) (domain.OtpMedium, error) {
}
return "", errors.New("both email and phone number are empty")
}
type SearchUserByNameOrPhoneReq struct {
SearchString string
}
// SearchUserByNameOrPhone godoc
// @Summary Search for user using name or phone
// @Description Search for user using name or phone
// @Tags user
// @Accept json
// @Produce json
// @Param searchUserByNameOrPhone body SearchUserByNameOrPhoneReq true "Search for using his name or phone"
// @Success 200 {object} UserProfileRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /user/search [post]
func SearchUserByNameOrPhone(logger *slog.Logger, userSvc *user.Service,
validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req SearchUserByNameOrPhoneReq
if err := c.BodyParser(&req); err != nil {
logger.Error("SearchUserByNameOrPhone failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
users, err := userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString)
if err != nil {
logger.Error("SearchUserByNameOrPhone failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Internal server error",
})
}
var res []UserProfileRes = make([]UserProfileRes, 0, len(users))
for _, user := range users {
res = append(res, UserProfileRes{
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,
})
}
return response.WriteJSON(c, fiber.StatusOK, "Search Successful", res, nil)
}
}

View File

@ -118,7 +118,7 @@ func GetAllWallets(logger *slog.Logger, walletSvc *wallet.Service, validator *cu
logger.Error("Failed to get wallets", "error", err)
}
var res []WalletRes = make([]WalletRes, len(wallets))
var res []WalletRes = make([]WalletRes, 0, len(wallets))
for _, wallet := range wallets {
res = append(res, convertWallet(wallet))

View File

@ -41,6 +41,7 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/user/profile", a.authMiddleware, handlers.UserProfile(a.logger, a.userSvc))
a.fiber.Get("/user/wallet", a.authMiddleware, handlers.GetCustomerWallet(a.logger, a.walletSvc, a.validator))
a.fiber.Post("/user/search", handlers.SearchUserByNameOrPhone(a.logger, a.userSvc, a.validator))
a.fiber.Get("/manager/:id/branch", handlers.GetBranchByManagerID(a.logger, a.branchSvc, a.validator))
@ -55,8 +56,10 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/branch/:id", handlers.GetBranchByID(a.logger, a.branchSvc, a.validator))
a.fiber.Put("/branch/:id", handlers.UpdateBranch(a.logger, a.branchSvc, a.validator))
a.fiber.Delete("/branch/:id", handlers.DeleteBranch(a.logger, a.branchSvc, a.validator))
// branch/wallet
// Branch Operation
a.fiber.Get("/supportedOperation", handlers.GetAllSupportedOperations(a.logger, a.branchSvc, a.validator))
a.fiber.Post("/supportedOperation", handlers.CreateSupportedOperation(a.logger, a.branchSvc, a.validator))
a.fiber.Post("/operation", handlers.CreateBranchOperation(a.logger, a.branchSvc, a.validator))
a.fiber.Get("/branch/:id/operation", handlers.GetBranchOperations(a.logger, a.branchSvc, a.validator))
@ -81,13 +84,13 @@ func (a *App) initAppRoutes() {
// Transfer
// /transfer/wallet - transfer from one wallet to another wallet
a.fiber.Post("/transfer/wallet", handlers.TransferToWallet(a.logger, a.walletSvc, a.branchSvc, a.validator))
a.fiber.Post("/transfer/wallet", a.authMiddleware, handlers.TransferToWallet(a.logger, a.walletSvc, a.branchSvc, a.validator))
// Transactions
a.fiber.Post("/transaction", handlers.CreateTransaction(a.logger, a.transactionSvc, a.validator))
a.fiber.Get("/transaction", handlers.GetAllTransactions(a.logger, a.transactionSvc, a.userSvc, a.validator))
a.fiber.Get("/transaction/:id", handlers.GetTransactionByID(a.logger, a.transactionSvc, a.validator))
a.fiber.Patch("/transaction/:id", handlers.UpdateTransactionVerified(a.logger, a.transactionSvc, a.validator))
a.fiber.Post("/transaction", a.authMiddleware, handlers.CreateTransaction(a.logger, a.transactionSvc, a.validator))
a.fiber.Get("/transaction", a.authMiddleware, handlers.GetAllTransactions(a.logger, a.transactionSvc, a.userSvc, a.validator))
a.fiber.Get("/transaction/:id", a.authMiddleware, handlers.GetTransactionByID(a.logger, a.transactionSvc, a.validator))
a.fiber.Patch("/transaction/:id", a.authMiddleware, handlers.UpdateTransactionVerified(a.logger, a.transactionSvc, a.validator))
a.fiber.Get("/notifications/ws/connect/:recipientID", handler.ConnectSocket)
a.fiber.Post("/notifications/mark-as-read", handler.MarkNotificationAsRead)