170 lines
3.5 KiB
Go
170 lines
3.5 KiB
Go
package worker
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/danilsolovyov/croupierCbrf/internal/dal"
|
|
"github.com/themakers/hlog"
|
|
"golang.org/x/text/encoding/charmap"
|
|
)
|
|
|
|
type worker struct {
|
|
ctx context.Context
|
|
mc *dal.MongoConnection
|
|
logger hlog.Logger
|
|
}
|
|
|
|
func InitAndStart(ctx context.Context, mc *dal.MongoConnection, logger hlog.Logger) error {
|
|
w := worker{ctx, mc, logger}
|
|
return w.start()
|
|
}
|
|
|
|
func (w *worker) start() error {
|
|
go func() {
|
|
for {
|
|
quotes, err := getQuotes()
|
|
|
|
if err != nil {
|
|
w.logger.Emit(ErrorWorker{err})
|
|
continue
|
|
}
|
|
|
|
date, err := time.Parse("02.01.2006 Z07", quotes.Date+" +03")
|
|
|
|
fmt.Println(date)
|
|
|
|
if err != nil {
|
|
w.logger.Emit(ErrorWorker{err})
|
|
continue
|
|
}
|
|
|
|
// Проверяем существование котировки USD и дату последнего обновления по ЦБ РФ
|
|
usd, err := w.mc.GetQuote(w.ctx, "USD")
|
|
|
|
if err != nil {
|
|
w.logger.Emit(ErrorWorker{err})
|
|
continue
|
|
}
|
|
|
|
// Если не находит или дата обновилась, то вставляем новые значения
|
|
if usd == nil || !date.Equal(usd.Date) {
|
|
for _, item := range quotes.Quotes {
|
|
quote, err := respQuoteToDalQuote(date, &item)
|
|
|
|
if err != nil {
|
|
w.logger.Emit(ErrorWorker{err})
|
|
continue
|
|
}
|
|
|
|
err = w.mc.UpdateQuote(w.ctx, quote)
|
|
if err != nil {
|
|
w.logger.Emit(ErrorWorker{err})
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
// Сверяем даты по дню - если не совпадают, то ставим таймер на 15 минут
|
|
|
|
nextTime := 15 * time.Minute
|
|
|
|
if date.Day() == now.In(time.FixedZone(date.Zone())).Day() {
|
|
next := date.AddDate(0, 0,
|
|
1).Add(time.Hour * 16) // Примерно в это время гарантированно обновляются котировки
|
|
nextTime = next.Sub(now)
|
|
}
|
|
|
|
select {
|
|
case <-w.ctx.Done():
|
|
return
|
|
case <-time.After(nextTime):
|
|
continue
|
|
}
|
|
}
|
|
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
type RespDailyQuotes struct {
|
|
Date string `xml:"Date,attr"`
|
|
Name string `xml:"name,attr"`
|
|
Quotes []RespQuote `xml:"Valute"`
|
|
}
|
|
|
|
type RespQuote struct {
|
|
ID string `xml:"ID,attr"`
|
|
NumCode int `xml:"NumCode"`
|
|
CharCode string `xml:"CharCode"`
|
|
Nominal int `xml:"Nominal"`
|
|
Name string `xml:"Name"`
|
|
Value string `xml:"Value"`
|
|
}
|
|
|
|
func getQuotes() (*RespDailyQuotes, error) {
|
|
resp, err := http.Get("https://cbr.ru/scripts/XML_daily.asp")
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, errors.New(fmt.Sprintf("bad status: %v", resp.Status))
|
|
}
|
|
|
|
var result RespDailyQuotes
|
|
|
|
d := xml.NewDecoder(resp.Body)
|
|
|
|
d.CharsetReader = identReader
|
|
|
|
err = d.Decode(&result)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
func identReader(encoding string, input io.Reader) (io.Reader, error) {
|
|
switch encoding {
|
|
case "windows-1251":
|
|
return charmap.Windows1251.NewDecoder().Reader(input), nil
|
|
}
|
|
return input, nil
|
|
}
|
|
|
|
func respQuoteToDalQuote(date time.Time, data *RespQuote) (*dal.Quote, error) {
|
|
strVal := strings.ReplaceAll(data.Value, ",", ".")
|
|
|
|
value, err := strconv.ParseFloat(strVal, 64)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &dal.Quote{
|
|
ID: data.CharCode,
|
|
Date: date,
|
|
IDCb: data.ID,
|
|
NumCode: data.NumCode,
|
|
Nominal: data.Nominal,
|
|
Name: data.Name,
|
|
Value: value,
|
|
}
|
|
|
|
return result, nil
|
|
}
|