2022-12-30 18:52:16 +00:00
|
|
|
|
package worker
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/templategen/amo"
|
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/templategen/dal"
|
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
|
|
|
|
|
gdisk "penahub.gitlab.yandexcloud.net/backend/templategen/gdisk"
|
2023-03-27 15:32:57 +00:00
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
|
2022-12-30 18:52:16 +00:00
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/templategen/templategen"
|
|
|
|
|
yadisk "penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
|
2023-04-17 22:38:59 +00:00
|
|
|
|
"fmt"
|
2022-12-30 18:52:16 +00:00
|
|
|
|
"reflect"
|
|
|
|
|
"strconv"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2023-01-06 19:41:50 +00:00
|
|
|
|
WorkerLifetime = time.Minute * 15
|
|
|
|
|
DebounceDuration = time.Millisecond * 165
|
|
|
|
|
CacheSize = 10
|
|
|
|
|
TaskTimeout = 5 * time.Minute
|
2022-12-30 18:52:16 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Worker struct {
|
|
|
|
|
AmoId string
|
|
|
|
|
Tube chan *model.WorkerTask // Буферизированный канал с размером CacheSize
|
|
|
|
|
ctx context.Context
|
|
|
|
|
lifeTimer *time.Timer
|
|
|
|
|
dal *dal.MongoDAL
|
|
|
|
|
yaDisk *yadisk.ClientApp
|
|
|
|
|
gDisk *gdisk.ClientApp
|
|
|
|
|
amo *amo.ClientApp
|
|
|
|
|
logger *zap.Logger
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewWorker(amoId string, ctx context.Context, dal *dal.MongoDAL, yaDisk *yadisk.ClientApp, gDisk *gdisk.ClientApp, amo *amo.ClientApp, logger *zap.Logger) *Worker {
|
|
|
|
|
return &Worker{
|
|
|
|
|
AmoId: amoId,
|
|
|
|
|
Tube: make(chan *model.WorkerTask, CacheSize),
|
|
|
|
|
ctx: ctx,
|
|
|
|
|
lifeTimer: time.NewTimer(WorkerLifetime),
|
|
|
|
|
dal: dal,
|
|
|
|
|
yaDisk: yaDisk,
|
|
|
|
|
gDisk: gDisk,
|
|
|
|
|
amo: amo,
|
|
|
|
|
logger: logger.With(zap.String("amo_id", amoId))}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 20:35:53 +00:00
|
|
|
|
func (w *Worker) Start(stopWorker chan string) {
|
2022-12-31 00:55:00 +00:00
|
|
|
|
w.logger.Info("worker started")
|
2022-12-30 18:52:16 +00:00
|
|
|
|
go func() {
|
2023-01-06 18:05:56 +00:00
|
|
|
|
select {
|
|
|
|
|
case <-w.ctx.Done():
|
|
|
|
|
w.logger.Info("worker stopped - context done")
|
|
|
|
|
stopWorker <- w.AmoId
|
|
|
|
|
return
|
|
|
|
|
case <-w.lifeTimer.C:
|
|
|
|
|
w.logger.Info("worker stopped - timeout")
|
|
|
|
|
stopWorker <- w.AmoId
|
|
|
|
|
return
|
2022-12-30 18:52:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}()
|
|
|
|
|
|
2022-12-31 00:55:00 +00:00
|
|
|
|
go func() {
|
|
|
|
|
for task := range w.Tube {
|
2023-01-06 19:41:50 +00:00
|
|
|
|
w.logger.Info("new task", zap.String("_id", task.ID))
|
2022-12-31 00:55:00 +00:00
|
|
|
|
if task == nil {
|
|
|
|
|
w.logger.Error("worker got empty task")
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-12-30 18:52:16 +00:00
|
|
|
|
|
2023-01-06 17:00:50 +00:00
|
|
|
|
err := w.dal.WorkerTask.UpdateStatus(w.ctx, task.ID, model.WorkerTaskStatusProcessing)
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.logger.Error("cannot update workerTask", zap.String("_id", task.ID))
|
|
|
|
|
}
|
2022-12-30 18:52:16 +00:00
|
|
|
|
|
2023-01-06 17:00:50 +00:00
|
|
|
|
task.Status = model.WorkerTaskStatusDone
|
2022-12-31 00:55:00 +00:00
|
|
|
|
if !w.DoTask(task) {
|
|
|
|
|
task.Status = model.WorkerTaskStatusFailed
|
2023-01-06 19:41:50 +00:00
|
|
|
|
w.logger.Info("task failed", zap.String("_id", task.ID))
|
2023-01-06 17:00:50 +00:00
|
|
|
|
} else {
|
2023-01-06 19:41:50 +00:00
|
|
|
|
w.logger.Info("task done", zap.String("_id", task.ID))
|
2022-12-31 00:55:00 +00:00
|
|
|
|
}
|
2022-12-30 18:52:16 +00:00
|
|
|
|
|
2023-01-06 17:00:50 +00:00
|
|
|
|
err = w.dal.WorkerTask.UpdateByID(w.ctx, task)
|
2023-01-06 19:41:50 +00:00
|
|
|
|
|
2022-12-31 00:55:00 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
w.logger.Error("cannot update workerTask", zap.String("_id", task.ID))
|
|
|
|
|
}
|
2022-12-30 18:52:16 +00:00
|
|
|
|
}
|
2022-12-31 00:55:00 +00:00
|
|
|
|
}()
|
2022-12-30 18:52:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *Worker) DoTask(task *model.WorkerTask) bool {
|
|
|
|
|
var err error
|
|
|
|
|
logger := w.logger.With(zap.String("_id", task.ID))
|
|
|
|
|
|
|
|
|
|
// сбрасываем таймер
|
|
|
|
|
w.lifeTimer.Stop()
|
|
|
|
|
w.lifeTimer.Reset(WorkerLifetime)
|
|
|
|
|
|
|
|
|
|
// находим историю к которой привязана задача
|
|
|
|
|
history, err := w.dal.History.GetByWorkerTaskID(w.ctx, task.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("get history", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 20:35:53 +00:00
|
|
|
|
defer func() {
|
|
|
|
|
err = w.dal.History.UpdateByID(w.ctx, history)
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.logger.Error("cannot update history", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2022-12-30 18:52:16 +00:00
|
|
|
|
// проверяем чтобы все поля были на месте
|
|
|
|
|
if task.LeadId <= 0 {
|
|
|
|
|
err = errors.New("got bad lead_id")
|
|
|
|
|
return w.checkErrTask(logger, history, "bad data", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if task.UserID == "" {
|
|
|
|
|
err = errors.New("got bad user_id")
|
|
|
|
|
return w.checkErrTask(logger, history, "bad data", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if task.Source.File == "" {
|
|
|
|
|
err = errors.New("got bad source.file")
|
|
|
|
|
return w.checkErrTask(logger, history, "bad data", err)
|
|
|
|
|
}
|
|
|
|
|
if task.Source.StorageID == "" {
|
|
|
|
|
err = errors.New("got bad source.storage_id")
|
|
|
|
|
return w.checkErrTask(logger, history, "bad data", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if task.Source.StorageType == "" {
|
|
|
|
|
err = errors.New("got bad source.storage_type")
|
|
|
|
|
return w.checkErrTask(logger, history, "bad data", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if task.Target.StorageID == "" {
|
|
|
|
|
err = errors.New("got bad target.storage_id")
|
|
|
|
|
return w.checkErrTask(logger, history, "bad data", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if task.Target.StorageType == "" {
|
|
|
|
|
err = errors.New("got bad target.storage_type")
|
|
|
|
|
return w.checkErrTask(logger, history, "bad data", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// amo client
|
|
|
|
|
|
|
|
|
|
amoData, err := w.dal.Amo.GetByID(w.ctx, task.AmoID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return w.checkErrTask(logger, history, "cannot get amo data", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
amoClient, err := w.amo.NewClient(w.ctx, amoData.Referer, amoData.Token(), "")
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return w.checkErrTask(logger, history, "cannot create amo client", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lead, err := DebounceWrapper(amoClient.GetLeadById)(strconv.FormatInt(task.LeadId, 10))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return w.checkErrTask(logger, history, "cannot get lead", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if lead == nil {
|
|
|
|
|
err = errors.New("empty lead data")
|
|
|
|
|
return w.checkErrTask(logger, history, "cannot get lead", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataTemplate := map[string]interface{}{}
|
|
|
|
|
|
|
|
|
|
// Добавляем Инфо Лида
|
|
|
|
|
for k, v := range templategen.AmoLeadFieldsToRuMap(lead) {
|
|
|
|
|
dataTemplate[k] = v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Добавялем инфо контактов
|
|
|
|
|
contacts := []amo.Contact{}
|
|
|
|
|
for _, data := range lead.Embedded.Contacts {
|
|
|
|
|
contact, err := DebounceWrapper(amoClient.GetContactById)(strconv.Itoa(data.Id))
|
|
|
|
|
if err == nil {
|
|
|
|
|
contacts = append(contacts, *contact)
|
|
|
|
|
} else {
|
|
|
|
|
return w.checkErrTask(logger, history, "cannot get contact", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataTemplate["Контакты"] = templategen.AmoContactsFieldsToRuMap(contacts)
|
|
|
|
|
|
|
|
|
|
// Добавляем инфо компаний
|
|
|
|
|
companies := []amo.Company{}
|
|
|
|
|
for _, data := range lead.Embedded.Companies {
|
|
|
|
|
company, err := DebounceWrapper(amoClient.GetCompanyById)(strconv.Itoa(data.Id))
|
|
|
|
|
if err == nil {
|
|
|
|
|
companies = append(companies, *company)
|
|
|
|
|
} else {
|
|
|
|
|
return w.checkErrTask(logger, history, "cannot get company", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataTemplate["Компании"] = templategen.AmoCompaniesFieldsToRuMap(companies)
|
|
|
|
|
|
2022-12-31 00:55:00 +00:00
|
|
|
|
err = w.Generate(task, lead.Name, dataTemplate)
|
2023-01-20 13:47:44 +00:00
|
|
|
|
|
2023-01-05 20:35:53 +00:00
|
|
|
|
history.Target.File = task.Target.File
|
|
|
|
|
history.Target.StorageID = task.Target.StorageID
|
|
|
|
|
history.Target.StorageType = task.Target.StorageType
|
2023-01-14 12:45:09 +00:00
|
|
|
|
history.DownloadUrl = task.DownloadUrl
|
|
|
|
|
history.PublicUrl = task.PublicUrl
|
2022-12-31 00:55:00 +00:00
|
|
|
|
|
2023-01-20 13:47:44 +00:00
|
|
|
|
return w.checkErrTask(logger, history, "cannot generate", err)
|
2022-12-30 18:52:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 18:51:39 +00:00
|
|
|
|
// checkErrTask - если найдена ошибка, пишет лог, добавляет в историю и возвращает false
|
2022-12-31 00:55:00 +00:00
|
|
|
|
func (w *Worker) checkErrTask(logger *zap.Logger, history *model.History, msg string, err error) bool {
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("task failed: "+msg, zap.Error(err))
|
2023-01-05 20:35:53 +00:00
|
|
|
|
if len(history.Errors) > 0 {
|
|
|
|
|
history.Errors = append(history.Errors, err.Error())
|
|
|
|
|
} else {
|
|
|
|
|
history.Errors = []string{err.Error()}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-31 00:55:00 +00:00
|
|
|
|
err = w.dal.History.UpdateByID(w.ctx, history)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("cannot update history", zap.Error(err))
|
|
|
|
|
}
|
2023-01-20 18:51:39 +00:00
|
|
|
|
return false
|
2022-12-31 00:55:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 18:51:39 +00:00
|
|
|
|
return true
|
2022-12-31 00:55:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-30 18:52:16 +00:00
|
|
|
|
// Generate - генерирует файл и отправляет его на указанный в task.source диск
|
|
|
|
|
// @TODO: после написания тестов сделать чтобы отправлял на диск task.target!!!
|
2022-12-31 00:55:00 +00:00
|
|
|
|
func (w *Worker) Generate(task *model.WorkerTask, name string, data any) error {
|
2022-12-30 18:52:16 +00:00
|
|
|
|
switch task.Source.StorageType {
|
|
|
|
|
case "gdisk":
|
|
|
|
|
gdiskData, err := w.dal.GDisk.GetByID(w.ctx, task.Source.StorageID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client, err := w.gDisk.NewClient(w.ctx, gdiskData.Token())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-31 00:55:00 +00:00
|
|
|
|
task.Target.File, task.DownloadUrl, err = templategen.GDiskGenerateDoc(task.Source.File, name,
|
2022-12-30 18:52:16 +00:00
|
|
|
|
task.UserID, gdiskData.SaveFolderID, client, data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "yadisk":
|
2023-04-17 23:11:18 +00:00
|
|
|
|
fmt.Println("YADI", task.Source.StorageID)
|
2022-12-30 18:52:16 +00:00
|
|
|
|
yaDiskData, err := w.dal.YaDisk.GetByID(w.ctx, task.Source.StorageID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client, err := w.yaDisk.NewClient(w.ctx, yaDiskData.Token(), "")
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-31 00:55:00 +00:00
|
|
|
|
task.Target.File, task.DownloadUrl, err = templategen.YaDiskGenerateDoc(task.Source.File, name,
|
2022-12-30 18:52:16 +00:00
|
|
|
|
task.UserID,
|
|
|
|
|
yaDiskData.SaveFolder, client, data)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-01-14 12:45:09 +00:00
|
|
|
|
|
|
|
|
|
err = client.PublishResource(task.Target.File)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := client.GetResources(task.Target.File, 0, 0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task.PublicUrl = res.PublicUrl
|
2023-03-27 15:32:57 +00:00
|
|
|
|
|
|
|
|
|
case "penadisk":
|
2023-04-17 22:27:49 +00:00
|
|
|
|
fmt.Println("ORAORA", task.UserID)
|
2023-03-27 15:32:57 +00:00
|
|
|
|
penadiskData, err := w.dal.PenaDisk.GetByUserID(w.ctx, task.UserID)
|
2023-04-17 22:27:49 +00:00
|
|
|
|
fmt.Println("ORAORA1", penadiskData, err)
|
2023-03-27 15:32:57 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client := penadisk.NewClient(task.UserID)
|
|
|
|
|
|
|
|
|
|
task.Target.File, task.DownloadUrl, err = templategen.PenaDiskGenerateDocBytes(task.Source.File, name,
|
|
|
|
|
task.UserID, penadiskData.SaveFolder, client, data)
|
|
|
|
|
|
2023-04-17 22:27:49 +00:00
|
|
|
|
fmt.Println("ORAORA2", task.Target.File, task.DownloadUrl)
|
2023-03-27 15:32:57 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := client.PublishResources(task.Target.File)
|
2023-04-17 22:27:49 +00:00
|
|
|
|
fmt.Println("ORAORA3", res, err)
|
2023-03-27 15:32:57 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task.PublicUrl = res
|
2022-12-30 18:52:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *Worker) Stop() {
|
|
|
|
|
close(w.Tube)
|
|
|
|
|
w.lifeTimer.Stop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func DebounceWrapper[T any](function T) T {
|
|
|
|
|
time.Sleep(DebounceDuration) // debounce
|
|
|
|
|
v := reflect.MakeFunc(reflect.TypeOf(function), func(in []reflect.Value) []reflect.Value {
|
|
|
|
|
f := reflect.ValueOf(function)
|
|
|
|
|
return f.Call(in)
|
|
|
|
|
})
|
|
|
|
|
return v.Interface().(T)
|
|
|
|
|
}
|