user

package
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package user is the business-logic layer for registered users — the people the platform serves and the staff who administer it. A user is identified by a mobile number, and carries a national ID, a KYC state, a role, and a lifecycle status. Service wraps the user repository and adds validation, uniqueness, the state machines below, and the guardrails that protect administrators from locking themselves out.

Build a Service with NewService. It creates users, looks them up (by ID, mobile number, or national ID), lists them with filters and pagination, and updates their profile, KYC, status, and role. It also exposes the counts the admin dashboards report on. Requests and responses are the DTOs in dto.go; failures are the sentinel errors in errors.go, which handlers map onto HTTP status codes. The mobile number and national ID are each unique across all users, and a duplicate is rejected at creation.

Three dimensions of state

A user's state is tracked along three independent axes:

  • KYC moves from pending to verified or rejected; a verified user can lapse to expired, and expired or rejected users return to pending to try again.
  • Status follows the same lifecycle as accounts — active, suspended, frozen, blocked, closed — with closed terminal.
  • Role is one of user, admin, or agent.

KYC and status changes are checked against a transition table and an illegal move is rejected with ErrInvalidKYCTransition or ErrInvalidStatusTransition.

Administrative guardrails

The privileged operations take the requester's ID alongside the target's, so the service can refuse changes that would compromise administration: a user cannot delete their own account (ErrCannotDeleteSelf), cannot change their own role (ErrCannotChangeOwnRole), and the last remaining admin cannot be deleted (ErrCannotDeleteLastAdmin). A deleted user cannot be modified until restored.

Transactions

CreateWithTx runs inside a *gorm.DB the caller owns, so a user can be created in the same database transaction as their first account.

Index

Constants

This section is empty.

Variables

View Source
var (
	// Resource not found errors
	ErrUserNotFound = errors.New("user not found")

	// Conflict errors
	ErrMobileNumberAlreadyExists = errors.New("mobile number already registered")
	ErrNationalIDAlreadyExists   = errors.New("national ID already registered")

	// Business logic errors
	ErrCannotDeleteLastAdmin   = errors.New("cannot delete the last admin")
	ErrInvalidKYCTransition    = errors.New("invalid KYC status transition")
	ErrCannotModifyDeletedUser = errors.New("cannot modify deleted user")
	ErrUserAlreadyDeleted      = errors.New("user is already deleted")
	ErrUserNotDeleted          = errors.New("user is not deleted")
	ErrInvalidStatusTransition = errors.New("invalid status transition")
	ErrCannotDeleteSelf        = errors.New("cannot delete your own account")
	ErrCannotChangeOwnRole     = errors.New("cannot change your own role")

	// Validation errors
	ErrInvalidInput        = errors.New("invalid input")
	ErrInvalidMobileNumber = errors.New("invalid mobile number format")
	ErrInvalidNationalID   = errors.New("invalid national ID format")
	ErrInvalidRole         = errors.New("invalid role")
	ErrInvalidKYCStatus    = errors.New("invalid KYC status")
	ErrInvalidUserStatus   = errors.New("invalid user status")
)

User service specific errors

Functions

This section is empty.

Types

type CreateUserRequest

type CreateUserRequest struct {
	MobileNumber      string       `json:"mobile_number" validate:"required"`
	CountryCode       string       `json:"country_code"`
	BirthDate         *models.Date `json:"birth_date,omitempty"`
	Address           string       `json:"address,omitempty"`
	City              string       `json:"city,omitempty"`
	PostalCode        string       `json:"postal_code,omitempty"`
	MobileNetworkCode string       `json:"mobile_network_code"`
	MomoNetworkCode   string       `json:"momo_network_code"`
	MomoNetworkName   string       `json:"momo_network_name,omitempty"`
	TelcoName         string       `json:"telco_name,omitempty"`
	FullName          string       `json:"full_name"`
	NationalID        string       `json:"national_id"`
	PreferredLanguage string       `json:"preferred_language,omitempty"`
	Role              string       `json:"role,omitempty"` // For admin creating users with specific roles
	// PinHash + PinSetAt let a user be created with a PIN already set, in one
	// atomic insert (USSD registration). Both nil for admin/API creation.
	PinHash  *string    `json:"-"`
	PinSetAt *time.Time `json:"-"`
}

CreateUserRequest represents the request to create a new user

type Service

type Service interface {
	// User management
	Create(ctx context.Context, req CreateUserRequest) (*UserResponse, error)
	CreateWithTx(ctx context.Context, tx *gorm.DB, req CreateUserRequest) (*UserResponse, error)
	GetByID(ctx context.Context, id string) (*UserResponse, error)
	GetByMobileNumber(ctx context.Context, mobileNumber string) (*UserResponse, error)
	GetByNationalID(ctx context.Context, nationalID string) (*UserResponse, error)

	// RebindMobileNumber moves an account to a new MSISDN. Used only by
	// new-SIM recovery, after ownership has been verified — it changes the
	// account's identity anchor.
	RebindMobileNumber(ctx context.Context, userID, mobileNumber string) error
	List(ctx context.Context, filters UserFilters, pagination services.Pagination) (*services.PaginatedResponse[UserResponse], error)
	Update(ctx context.Context, id string, req UpdateUserRequest) (*UserResponse, error)
	Delete(ctx context.Context, requesterID, targetID string) error
	Restore(ctx context.Context, requesterID, targetID string) error

	// KYC management
	UpdateKYCStatus(ctx context.Context, adminID, userID string, req UpdateKYCStatusRequest) (*UserResponse, error)

	// Status management
	UpdateUserStatus(ctx context.Context, adminID, userID string, req UpdateUserStatusRequest) (*UserResponse, error)

	// Role management
	UpdateUserRole(ctx context.Context, adminID, userID string, req UpdateUserRoleRequest) (*UserResponse, error)

	// Admin operations and analytics
	Count(ctx context.Context) (int64, error)
	CountByKYCStatus(ctx context.Context, kycStatus string) (int64, error)
	CountByRole(ctx context.Context, role string) (int64, error)
	CountAdmins(ctx context.Context) (int, error)
}

Service defines the interface for user business logic operations

func NewService

func NewService(repo repository.UserRepository) Service

NewService creates a new user service instance

type UpdateKYCStatusRequest

type UpdateKYCStatusRequest struct {
	KYCStatus string `json:"kyc_status" validate:"required,oneof=pending in_progress verified rejected"`
}

UpdateKYCStatusRequest represents the request to update user KYC status

type UpdateUserRequest

type UpdateUserRequest struct {
	FullName          *string      `json:"full_name,omitempty"`
	NationalID        *string      `json:"national_id,omitempty"`
	BirthDate         *models.Date `json:"birth_date,omitempty"`
	Address           *string      `json:"address,omitempty"`
	City              *string      `json:"city,omitempty"`
	PostalCode        *string      `json:"postal_code,omitempty"`
	PreferredLanguage *string      `json:"preferred_language,omitempty"`
}

UpdateUserRequest represents the request to update user information

type UpdateUserRoleRequest

type UpdateUserRoleRequest struct {
	Role string `json:"role" validate:"required,oneof=user admin agent"`
}

UpdateUserRoleRequest represents the request to update user role

type UpdateUserStatusRequest

type UpdateUserStatusRequest struct {
	Status string `json:"status" validate:"required,oneof=active suspended inactive"`
}

UpdateUserStatusRequest represents the request to update user status

type UserFilters

type UserFilters struct {
	KYCStatus string `json:"kyc_status,omitempty"`
	Role      string `json:"role,omitempty"`
	Status    string `json:"status,omitempty"`
}

UserFilters represents the filters for listing users

type UserResponse

type UserResponse struct {
	ID                string       `json:"id"`
	MobileNumber      string       `json:"mobile_number"`
	CountryCode       string       `json:"country_code"`
	BirthDate         *models.Date `json:"birth_date,omitempty"`
	Address           string       `json:"address,omitempty"`
	City              string       `json:"city,omitempty"`
	PostalCode        string       `json:"postal_code,omitempty"`
	MobileNetworkCode string       `json:"mobile_network_code"`
	MomoNetworkCode   string       `json:"momo_network_code"`
	MomoNetworkName   string       `json:"momo_network_name,omitempty"`
	TelcoName         string       `json:"telco_name,omitempty"`
	FullName          string       `json:"full_name,omitempty"`
	NationalID        string       `json:"national_id,omitempty"`
	KYCStatus         string       `json:"kyc_status"`
	KYCVerifiedAt     *time.Time   `json:"kyc_verified_at,omitempty"`
	PreferredLanguage string       `json:"preferred_language"`
	Status            string       `json:"status"`
	Role              string       `json:"role"`
	CreatedAt         time.Time    `json:"created_at"`
	UpdatedAt         time.Time    `json:"updated_at"`
}

UserResponse represents the response containing user information

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL