This commit is contained in:
r8zavetr8v 2024-05-18 15:01:12 +03:00
parent 876957aabe
commit 866a862087
19 changed files with 645 additions and 45 deletions

9
.vscode/launch.json vendored
View File

@ -12,15 +12,18 @@
"program": "${workspaceRoot}/backend/cmd/main.go",
"args": [
"-log-level=debug",
"-log-local=true",
"-log-local=false",
"-log-add-source=true",
"-jwt-secret=local_jwt_secret",
"-rest-address=localhost:8080",
"-rest-address=localhost:8081",
"-db-host=localhost:8432",
"-db-database=blockd",
"-db-user=blockd",
"-db-secret=blockd",
"-db-enable-tls=false"
"-db-enable-tls=false",
"-cache-host=localhost:6379"
]
}
]

View File

@ -8,4 +8,3 @@
- On First Login - Owner inputs his SEED_KEY (mnemonic), creates an organization, we save its seed hash for future login and signing internal txs.
- When inviting an employee to organization- we generate an invitation link, then after clicking on this link - the user is asked for seed, if he's already registered or able to generate a seed for new account.
-

View File

@ -1,7 +1,4 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{

View File

@ -6,10 +6,12 @@ import (
"github.com/emochka2007/block-accounting/internal/pkg/config"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/jwt"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/organizations"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/transactions"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
"github.com/emochka2007/block-accounting/internal/usecase/repository/auth"
"github.com/emochka2007/block-accounting/internal/usecase/repository/cache"
orepo "github.com/emochka2007/block-accounting/internal/usecase/repository/organizations"
txRepo "github.com/emochka2007/block-accounting/internal/usecase/repository/transactions"
urepo "github.com/emochka2007/block-accounting/internal/usecase/repository/users"
)
@ -35,3 +37,15 @@ func provideOrganizationsInteractor(
) organizations.OrganizationsInteractor {
return organizations.NewOrganizationsInteractor(log, orgRepo, cache)
}
func provideTxInteractor(
log *slog.Logger,
txRepo txRepo.Repository,
orgInteractor organizations.OrganizationsInteractor,
) transactions.TransactionsInteractor {
return transactions.NewTransactionsInteractor(
log.WithGroup("transaction-interactor"),
txRepo,
orgInteractor,
)
}

View File

@ -13,6 +13,7 @@ import (
"github.com/emochka2007/block-accounting/internal/pkg/logger"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/jwt"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/organizations"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/transactions"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
)
@ -20,6 +21,7 @@ var interfaceSet wire.ProviderSet = wire.NewSet(
provideAuthController,
provideOrganizationsController,
provideControllers,
provideTxController,
provideAuthPresenter,
provideOrganizationsPresenter,
@ -84,15 +86,27 @@ func provideOrganizationsController(
)
}
func provideTxController(
log *slog.Logger,
txInteractor transactions.TransactionsInteractor,
) controllers.TransactionsController {
return controllers.NewTransactionsController(
log.WithGroup("transactions-controller"),
txInteractor,
)
}
func provideControllers(
log *slog.Logger,
authController controllers.AuthController,
orgController controllers.OrganizationsController,
txController controllers.TransactionsController,
) *controllers.RootController {
return &controllers.RootController{
Ping: controllers.NewPingController(log.WithGroup("ping-controller")),
Auth: authController,
Organizations: orgController,
Transactions: txController,
}
}

View File

@ -8,6 +8,7 @@ import (
"github.com/emochka2007/block-accounting/internal/usecase/repository/auth"
"github.com/emochka2007/block-accounting/internal/usecase/repository/cache"
"github.com/emochka2007/block-accounting/internal/usecase/repository/organizations"
"github.com/emochka2007/block-accounting/internal/usecase/repository/transactions"
"github.com/emochka2007/block-accounting/internal/usecase/repository/users"
"github.com/redis/go-redis/v9"
)
@ -16,8 +17,15 @@ func provideUsersRepository(db *sql.DB) users.Repository {
return users.NewRepository(db)
}
func provideOrganizationsRepository(db *sql.DB) organizations.Repository {
return organizations.NewRepository(db)
func provideOrganizationsRepository(
db *sql.DB,
uRepo users.Repository,
) organizations.Repository {
return organizations.NewRepository(db, uRepo)
}
func provideTxRepository(db *sql.DB) transactions.Repository {
return transactions.NewRepository(db)
}
func provideAuthRepository(db *sql.DB) auth.Repository {

View File

@ -18,8 +18,10 @@ func ProvideService(c config.Config) (service.Service, func(), error) {
provideRedisCache,
provideUsersRepository,
provideUsersInteractor,
provideTxRepository,
provideOrganizationsRepository,
provideOrganizationsInteractor,
provideTxInteractor,
provideAuthRepository,
provideJWTInteractor,
interfaceSet,

View File

@ -26,13 +26,16 @@ func ProvideService(c config.Config) (service.Service, func(), error) {
jwtInteractor := provideJWTInteractor(c, usersInteractor, authRepository)
authPresenter := provideAuthPresenter(jwtInteractor)
authController := provideAuthController(logger, usersInteractor, authPresenter, jwtInteractor)
organizationsRepository := provideOrganizationsRepository(db)
organizationsRepository := provideOrganizationsRepository(db, usersRepository)
client, cleanup2 := provideRedisConnection(c)
cache := provideRedisCache(client, logger)
organizationsInteractor := provideOrganizationsInteractor(logger, organizationsRepository, cache)
organizationsPresenter := provideOrganizationsPresenter()
organizationsController := provideOrganizationsController(logger, organizationsInteractor, organizationsPresenter)
rootController := provideControllers(logger, authController, organizationsController)
transactionsRepository := provideTxRepository(db)
transactionsInteractor := provideTxInteractor(logger, transactionsRepository, organizationsInteractor)
transactionsController := provideTxController(logger, transactionsInteractor)
rootController := provideControllers(logger, authController, organizationsController, transactionsController)
server := provideRestServer(logger, rootController, c, jwtInteractor)
serviceService := service.NewService(logger, server)
return serviceService, func() {

View File

@ -9,13 +9,14 @@ import (
"github.com/emochka2007/block-accounting/internal/interface/rest/domain"
"github.com/emochka2007/block-accounting/internal/interface/rest/presenters"
"github.com/emochka2007/block-accounting/internal/pkg/ctxmeta"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/organizations"
)
type OrganizationsController interface {
NewOrganization(w http.ResponseWriter, r *http.Request) ([]byte, error)
ListOrganizations(w http.ResponseWriter, r *http.Request) ([]byte, error)
// todo delete
// todo update
}
type organizationsController struct {
@ -66,13 +67,7 @@ func (c *organizationsController) ListOrganizations(w http.ResponseWriter, r *ht
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()
user, err := ctxmeta.User(ctx)
if err != nil {
return nil, fmt.Errorf("error fetch user from context. %w", err)
}
resp, err := c.orgInteractor.List(ctx, organizations.ListParams{
UserId: user.Id(),
Cursor: req.Cursor,
Limit: req.Limit,
OffsetDate: time.UnixMilli(req.OffsetDate),

View File

@ -4,16 +4,19 @@ type RootController struct {
Ping PingController
Auth AuthController
Organizations OrganizationsController
Transactions TransactionsController
}
func NewRootController(
ping PingController,
auth AuthController,
organizations OrganizationsController,
transactions TransactionsController,
) *RootController {
return &RootController{
Ping: ping,
Auth: auth,
Organizations: organizations,
Transactions: transactions,
}
}

View File

@ -0,0 +1,36 @@
package controllers
import (
"log/slog"
"net/http"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/transactions"
)
type TransactionsController interface {
New(w http.ResponseWriter, r *http.Request) ([]byte, error)
List(w http.ResponseWriter, r *http.Request) ([]byte, error)
}
type transactionsController struct {
log *slog.Logger
txInteractor transactions.TransactionsInteractor
}
func NewTransactionsController(
log *slog.Logger,
txInteractor transactions.TransactionsInteractor,
) TransactionsController {
return &transactionsController{
log: log,
txInteractor: txInteractor,
}
}
func (c *transactionsController) New(w http.ResponseWriter, r *http.Request) ([]byte, error) {
panic("implement me!")
}
func (c *transactionsController) List(w http.ResponseWriter, r *http.Request) ([]byte, error) {
panic("implement me!")
}

View File

@ -109,8 +109,8 @@ func (s *Server) buildRouter() {
// r.Delete("/", s.handle(s.controllers.Organizations.NewOrganization, "delete_organization"))
r.Route("/transactions", func(r chi.Router) {
r.Get("/", nil) // list todo add cache
r.Post("/", nil) // add
r.Get("/", s.handle(s.controllers.Transactions.List, "tx_list"))
r.Post("/", s.handle(s.controllers.Transactions.New, "new_tx"))
r.Put("/{tx_id}", nil) // update / approve (or maybe body?)
r.Delete("/{tx_id}", nil) // remove
})

View File

@ -11,7 +11,7 @@ type Transaction struct {
Description string
OrganizationId uuid.UUID
CreatedBy *User
CreatedBy *OrganizationUser
Amount int64
ToAddr []byte

View File

@ -60,7 +60,7 @@ const (
)
type OrganizationParticipant interface {
UserIdentity
Id() uuid.UUID
Type() OrganizationParticipantType
@ -102,12 +102,21 @@ func (u *OrganizationUser) Position() string {
type Employee struct {
ID uuid.UUID
UserID uuid.UUID
OrganizationId uuid.UUID
WalletAddress []byte
CreatedAt time.Time
UpdatedAt time.Time
}
func (u *Employee) Id() uuid.UUID {
return u.ID
}
func (u *Employee) UserId() uuid.UUID {
return u.UserID
}
func (u *Employee) Type() OrganizationParticipantType {
return OrganizationParticipantTypeEmployee
}

View File

@ -37,6 +37,24 @@ type ListParams struct {
Limit uint8 // Max limit is 50 (may change)
}
type ParticipantParams struct {
ID uuid.UUID
OrganizationID uuid.UUID
UsersOnly bool
ActiveOnly bool
EmployeesOnly bool
}
type ParticipantsParams struct {
IDs uuid.UUIDs
OrganizationID uuid.UUID
UsersOnly bool
ActiveOnly bool
EmployeesOnly bool
}
type OrganizationsInteractor interface {
Create(
ctx context.Context,
@ -46,6 +64,14 @@ type OrganizationsInteractor interface {
ctx context.Context,
params ListParams,
) (*ListResponse, error)
Participant(
ctx context.Context,
params ParticipantParams,
) (models.OrganizationParticipant, error)
Participants(
ctx context.Context,
params ParticipantsParams,
) ([]models.OrganizationParticipant, error)
}
type organizationsInteractor struct {
@ -226,3 +252,62 @@ func (i *organizationsInteractor) Create(
return &org, nil
}
func (i *organizationsInteractor) Participant(
ctx context.Context,
params ParticipantParams,
) (models.OrganizationParticipant, error) {
participants, err := i.Participants(ctx, ParticipantsParams{
IDs: uuid.UUIDs{params.ID},
OrganizationID: params.OrganizationID,
ActiveOnly: params.ActiveOnly,
UsersOnly: params.UsersOnly,
EmployeesOnly: params.EmployeesOnly,
})
if err != nil {
return nil, fmt.Errorf("error fetch organization participant. %w", err)
}
if len(participants) == 0 {
return nil, fmt.Errorf("error organization participant empty. %w", err)
}
return participants[0], nil
}
func (i *organizationsInteractor) Participants(
ctx context.Context,
params ParticipantsParams,
) ([]models.OrganizationParticipant, error) {
// TODO check access for ctx user
user, err := ctxmeta.User(ctx)
if err != nil {
return nil, fmt.Errorf("error fetch user from context. %w", err)
}
_, err = i.orgRepository.Participants(ctx, organizations.ParticipantsParams{
Ids: uuid.UUIDs{user.Id()},
OrganizationId: params.OrganizationID,
ActiveOnly: params.ActiveOnly,
UsersOnly: true,
})
if err != nil {
return nil, errors.Join(
fmt.Errorf("error fetch organization user. %w", err),
ErrorUnauthorizedAccess,
)
}
participants, err := i.orgRepository.Participants(ctx, organizations.ParticipantsParams{
Ids: params.IDs,
OrganizationId: params.OrganizationID,
UsersOnly: params.UsersOnly,
EmployeesOnly: params.EmployeesOnly,
ActiveOnly: params.ActiveOnly,
})
if err != nil {
return nil, fmt.Errorf("error fetch organization participants. %w", err)
}
return participants, nil
}

View File

@ -0,0 +1,140 @@
package transactions
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/emochka2007/block-accounting/internal/pkg/ctxmeta"
"github.com/emochka2007/block-accounting/internal/pkg/models"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/organizations"
"github.com/emochka2007/block-accounting/internal/usecase/repository/transactions"
"github.com/google/uuid"
)
type ListParams struct {
IDs uuid.UUIDs
OrganizationID uuid.UUID
CreatedBy uuid.UUID
To []byte
Limit int64
Cursor string
WithCancelled bool
WithConfirmed bool
WithCommited bool
WithExpired bool
WithConfirmations bool
}
type CreateParams struct {
Tx models.Transaction
OrganizationId uuid.UUID
}
type ConfirmParams struct {
TxID uuid.UUID
OrganizationID uuid.UUID
}
type CancelParams struct {
TxID uuid.UUID
OrganizationID uuid.UUID
Cause string
}
type TransactionsInteractor interface {
List(ctx context.Context, params ListParams) ([]*models.Transaction, error)
Create(ctx context.Context, params CreateParams) (*models.Transaction, error)
Confirm(ctx context.Context, params ConfirmParams) (*models.Transaction, error)
Cancel(ctx context.Context, params CancelParams) (*models.Transaction, error)
// TODO delete
// TODO update
}
type transactionsInteractor struct {
log *slog.Logger
txRepo transactions.Repository
orgInteractor organizations.OrganizationsInteractor
}
func NewTransactionsInteractor(
log *slog.Logger,
txRepo transactions.Repository,
orgInteractor organizations.OrganizationsInteractor,
) TransactionsInteractor {
return &transactionsInteractor{
log: log,
txRepo: txRepo,
orgInteractor: orgInteractor,
}
}
func (i *transactionsInteractor) List(ctx context.Context, params ListParams) ([]*models.Transaction, error) {
filters := make([]transactions.GetTransactionsFilter, 0)
if params.WithCancelled {
filters = append(filters, transactions.GetFilterCancelled)
}
if params.WithConfirmed {
filters = append(filters, transactions.GetFilterConfirmed)
}
if params.WithCommited {
filters = append(filters, transactions.GetFilterCommited)
}
txs, err := i.txRepo.GetTransactions(ctx, transactions.GetTransactionsParams{
Ids: params.IDs,
OrganizationId: params.OrganizationID,
CreatedById: params.CreatedBy,
Filters: filters,
})
if err != nil {
return nil, fmt.Errorf("error fetch transaction from repository. %w", err)
}
return txs, nil
}
func (i *transactionsInteractor) Create(
ctx context.Context,
params CreateParams,
) (*models.Transaction, error) {
user, err := ctxmeta.User(ctx)
if err != nil {
return nil, fmt.Errorf("error fetch user from context. %w", err)
}
tx := params.Tx
participant, err := i.orgInteractor.Participant(ctx, organizations.ParticipantParams{
ID: user.Id(),
ActiveOnly: true,
UsersOnly: true,
})
if err != nil {
return nil, fmt.Errorf("error fetch actor prticipant. %w", err)
}
tx.CreatedBy = participant.GetUser()
tx.CreatedAt = time.Now()
if err = i.txRepo.CreateTransaction(ctx, tx); err != nil {
return nil, fmt.Errorf("error create new tx. %w", err)
}
return &tx, nil
}
func (i *transactionsInteractor) Confirm(ctx context.Context, params ConfirmParams) (*models.Transaction, error) {
panic("implement me!")
}
func (i *transactionsInteractor) Cancel(ctx context.Context, params CancelParams) (*models.Transaction, error) {
panic("implement me!")
}

View File

@ -10,7 +10,13 @@ import (
sq "github.com/Masterminds/squirrel"
"github.com/emochka2007/block-accounting/internal/pkg/models"
sqltools "github.com/emochka2007/block-accounting/internal/pkg/sqlutils"
"github.com/emochka2007/block-accounting/internal/usecase/repository/users"
"github.com/google/uuid"
"golang.org/x/sync/errgroup"
)
var (
ErrorNotFound = errors.New("not found")
)
type GetParams struct {
@ -51,17 +57,23 @@ type Repository interface {
Update(ctx context.Context, org models.Organization) error
Delete(ctx context.Context, id uuid.UUID) error
AddParticipant(ctx context.Context, params AddParticipantParams) error
Participants(ctx context.Context, params ParticipantsParams) ([]models.OrganizationParticipant, error)
CreateAndAdd(ctx context.Context, org models.Organization, user *models.User) error
DeleteParticipant(ctx context.Context, params DeleteParticipantParams) error
}
type repositorySQL struct {
db *sql.DB
db *sql.DB
usersRepository users.Repository
}
func NewRepository(db *sql.DB) Repository {
func NewRepository(
db *sql.DB,
usersRepository users.Repository,
) Repository {
return &repositorySQL{
db: db,
db: db,
usersRepository: usersRepository,
}
}
@ -262,22 +274,6 @@ func (r *repositorySQL) CreateAndAdd(ctx context.Context, org models.Organizatio
return nil
}
func (r *repositorySQL) Participants(
ctx context.Context,
params ParticipantsParams,
) ([]models.OrganizationParticipant, error) {
participants := make([]models.OrganizationParticipant, 0, len(params.Ids))
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
return nil
}); err != nil {
}
return participants, nil
}
func (r *repositorySQL) AddParticipant(ctx context.Context, params AddParticipantParams) error {
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
query := sq.Insert("organizations_users").
@ -349,3 +345,291 @@ func (r *repositorySQL) DeleteParticipant(ctx context.Context, params DeletePart
return nil
}
func (r *repositorySQL) Participants(
ctx context.Context,
params ParticipantsParams,
) ([]models.OrganizationParticipant, error) {
participants := make([]models.OrganizationParticipant, 0, len(params.Ids))
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
orgUsersModels, err := r.fetchOrganizationUsers(ctx, params)
if err != nil {
return fmt.Errorf("error fetch organization users raw models. %w", err)
}
eg, egCtx := errgroup.WithContext(ctx)
var employees []*models.Employee = make([]*models.Employee, 0, len(orgUsersModels))
eg.Go(func() error {
ids := make(uuid.UUIDs, 0, len(orgUsersModels))
for _, m := range orgUsersModels {
if m.employeeID != uuid.Nil {
ids = append(ids, m.employeeID)
}
}
employees, err = r.fetchEmployees(egCtx, fetchEmployeesParams{
IDs: ids,
OrganizationId: params.OrganizationId,
})
if err != nil {
return fmt.Errorf("error fetch employees. %w", err)
}
return nil
})
var usrs []*models.User
eg.Go(func() error {
ids := make(uuid.UUIDs, 0, len(orgUsersModels))
for _, m := range orgUsersModels {
if m.userID != uuid.Nil {
ids = append(ids, m.employeeID)
}
}
usrs, err = r.usersRepository.Get(egCtx, users.GetParams{
Ids: ids,
})
if err != nil {
return fmt.Errorf("error fetch users by ids. %w", err)
}
return nil
})
if err = eg.Wait(); err != nil {
return fmt.Errorf("error organizations users entitiels. %w", err)
}
for _, ou := range orgUsersModels {
var employee *models.Employee
if ou.employeeID != uuid.Nil {
for _, e := range employees {
if e.ID == ou.employeeID {
employee = e
break
}
}
}
if ou.userID == uuid.Nil && employee != nil {
participants = append(participants, employee)
}
var orgUser *models.OrganizationUser
for _, u := range usrs {
if u.Id() == ou.userID {
orgUser = &models.OrganizationUser{
User: *u,
OrgPosition: ou.position,
Admin: ou.isAdmin,
Employee: employee,
}
}
}
participants = append(participants, orgUser)
}
return nil
}); err != nil {
return nil, fmt.Errorf("error execute transactional operation. %w", err)
}
if len(participants) == 0 {
return nil, ErrorNotFound
}
return participants, nil
}
type fetchOrganizationUsersModel struct {
organizationID uuid.UUID
userID uuid.UUID
employeeID uuid.UUID
position string
addedAt time.Time
updatedAt time.Time
deletedAt time.Time
isAdmin bool
}
func (r *repositorySQL) fetchOrganizationUsers(
ctx context.Context,
params ParticipantsParams,
) ([]fetchOrganizationUsersModel, error) {
participants := make([]fetchOrganizationUsersModel, 0, len(params.Ids))
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
ouQuery := sq.Select(
"ou.organization_id",
"ou.user_id",
"ou.employee_id",
"ou.position",
"ou.added_at",
"ou.updated_at",
"ou.deleted_at",
"ou.is_admin",
).Where(sq.Eq{
"ou.organization_id": params.OrganizationId,
}).PlaceholderFormat(sq.Dollar)
if len(params.Ids) > 0 {
ouQuery = ouQuery.Where(sq.Eq{
"ou.user_id": params.Ids,
})
}
rows, err := ouQuery.RunWith(r.Conn(ctx)).QueryContext(ctx)
if err != nil {
return fmt.Errorf("error fetch organization participants. %w", err)
}
defer func() {
if closeErr := rows.Close(); closeErr != nil {
err = errors.Join(fmt.Errorf("error close rows. %w", closeErr), err)
}
}()
for rows.Next() {
var (
organizationID uuid.UUID
userID uuid.UUID
employeeID uuid.UUID
position string
addedAt time.Time
updatedAt time.Time
deletedAt sql.NullTime
isAdmin bool
)
if err = rows.Scan(
&organizationID,
&userID,
&employeeID,
&position,
&addedAt,
&updatedAt,
&deletedAt,
&isAdmin,
); err != nil {
return fmt.Errorf("error scan row. %w", err)
}
if params.EmployeesOnly && employeeID == uuid.Nil {
continue
}
if params.UsersOnly && userID == uuid.Nil {
continue
}
if params.ActiveOnly && deletedAt.Valid {
continue
}
participants = append(participants, fetchOrganizationUsersModel{
organizationID: organizationID,
userID: userID,
employeeID: employeeID,
position: position,
addedAt: addedAt,
updatedAt: updatedAt,
deletedAt: deletedAt.Time,
isAdmin: isAdmin,
})
}
return err
}); err != nil {
return nil, fmt.Errorf("error execute transactional operation. %w", err)
}
return participants, nil
}
type fetchEmployeesParams struct {
IDs uuid.UUIDs
OrganizationId uuid.UUID
}
func (r *repositorySQL) fetchEmployees(
ctx context.Context,
params fetchEmployeesParams,
) ([]*models.Employee, error) {
employees := make([]*models.Employee, 0, len(params.IDs))
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
query := sq.Select(
"e.id",
"e.user_id",
"e.organization_id",
"e.wallet_address",
"e.created_at",
"e.updated_at",
).Where(sq.Eq{
"e.organization_id": params.OrganizationId,
}).PlaceholderFormat(sq.Dollar)
if len(params.IDs) > 0 {
query = query.Where(sq.Eq{
"e.id": params.IDs,
})
}
rows, err := query.RunWith(r.Conn(ctx)).QueryContext(ctx)
if err != nil {
return fmt.Errorf("error fetch employees from database. %w", err)
}
defer func() {
if closeErr := rows.Close(); closeErr != nil {
err = errors.Join(fmt.Errorf("error close rows. %w", closeErr), err)
}
}()
for rows.Next() {
var (
id uuid.UUID
userID uuid.UUID
orgID uuid.UUID
walletAddr []byte
createdAt time.Time
updatedAt time.Time
)
if err = rows.Scan(
&id,
&userID,
&orgID,
&walletAddr,
&createdAt,
&updatedAt,
); err != nil {
return fmt.Errorf("error scan row. %w", err)
}
employees = append(employees, &models.Employee{
ID: id,
UserID: userID,
OrganizationId: orgID,
WalletAddress: walletAddr,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
})
}
return nil
}); err != nil {
return nil, fmt.Errorf("error execute transactional operation. %w", err)
}
return employees, nil
}

View File

@ -143,9 +143,11 @@ func (r *repositorySQL) GetTransactions(
Amount: amount,
ToAddr: toAddr,
MaxFeeAllowed: maxFeeAllowed,
CreatedBy: &models.User{
ID: createdById,
Bip39Seed: createdBySeed,
CreatedBy: &models.OrganizationUser{
User: models.User{
ID: createdById,
Bip39Seed: createdBySeed,
},
},
CreatedAt: createdAt,
UpdatedAt: updatedAt,

View File

@ -54,7 +54,7 @@ create index if not exists index_organizations_id
create table employees (
id uuid primary key,
user_id uuid references users(id),
user_id uuid,
organization_id uuid not null references organizations(id),
wallet_address text not null,
created_at timestamp default current_timestamp,
@ -97,6 +97,7 @@ create table if not exists transactions (
amount bigint default 0,
to_addr bytea not null,
tx_index bytea default null,
max_fee_allowed bigint default 0,
deadline timestamp default null,
@ -138,9 +139,14 @@ create table contracts (
address bytea not null,
payload bytea not null,
created_by uuid not null references users(id),
organization_id uuid not null references organizations(id),
status tinyint default 0,
tx_index bytea default null,
created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp
);