/
buy.go
123 lines (91 loc) · 2.84 KB
/
buy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package my
import (
"context"
"errors"
"fmt"
"github.com/modern-questions-team-13/orange-stock-market/internal/model"
"github.com/modern-questions-team-13/orange-stock-market/internal/repository"
"github.com/modern-questions-team-13/orange-stock-market/internal/repository/repoerrs"
"github.com/opentracing/opentracing-go"
"github.com/rs/zerolog/log"
)
const tryBuyLimit = 2
type Buy struct {
repos *repository.Repositories
}
func (b *Buy) Create(ctx context.Context, userId, companyId int, price int) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "app: create buy")
defer span.Finish()
err := b.repos.User.Withdraw(ctx, userId, price)
if err != nil {
return err
}
ok, err := b.tryBuyImmediately(ctx, userId, companyId, price)
if err != nil {
if !ok {
log.Err(err).Msg("immediate buy")
return err
}
_, _err := b.repos.Buy.Create(ctx, userId, companyId, price)
if _err != nil {
return err
}
log.Info().Msg(err.Error())
}
return nil
}
func (b *Buy) GetAllBuysByCompanyId(ctx context.Context, companyId int, limit, offset uint64) (price []int, err error) {
return b.repos.GetAllBuys(ctx, companyId, limit, offset)
}
func (b *Buy) tryBuyImmediately(ctx context.Context, userId, companyId int, price int) (bool, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "app: try buy immediately")
defer span.Finish()
sales, err := b.repos.Sale.GetSales(ctx, companyId, price, tryBuyLimit)
if err != nil {
return false, err
}
span, ctx = opentracing.StartSpanFromContext(ctx, "app: range in buy")
for _, saleId := range sales {
if sale, _err := b.repos.Sale.Delete(ctx, saleId); _err == nil {
err = b.makeBuyOperation(ctx, userId, sale, price)
if err != nil {
return false, err
}
return true, nil
}
}
span.Finish()
return true, fmt.Errorf("not finding matching sales")
}
func (b *Buy) addStock(ctx context.Context, userId, companyId int) error {
err := b.repos.Portfolio.Create(ctx, userId, companyId, 1)
if err != nil {
if errors.Is(err, repoerrs.ErrAlreadyExists) {
return b.repos.Portfolio.AddStock(ctx, userId, companyId)
}
}
return err
}
func (b *Buy) makeBuyOperation(ctx context.Context, buyerId int, sale model.Sale, reservedMoney int) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "app: makes buy operation")
defer span.Finish()
err := b.repos.User.TopUp(ctx, sale.UserId, sale.Price)
if err != nil {
_ = b.addStock(ctx, sale.UserId, sale.CompanyId)
return err
}
if sale.Price < reservedMoney {
err = b.repos.User.TopUp(ctx, buyerId, reservedMoney-sale.Price)
if err != nil {
return err
}
}
err = b.addStock(ctx, buyerId, sale.CompanyId)
if err != nil {
return err
}
return b.repos.Operation.Create(ctx, buyerId, sale.UserId, sale.CompanyId, sale.Price)
}
func NewBuy(repos *repository.Repositories) *Buy {
return &Buy{repos: repos}
}