package donat import ( "context" "donat-widget/internal/model" "donat-widget/internal/model/api" "github.com/google/uuid" "github.com/labstack/echo/v4" "log/slog" "mime/multipart" "net/http" ) type ServiceDonat struct { donatRepo model.DonatRepo widgetRepo model.WidgetRepo authClient model.AuthClient paymentClient model.PaymentClient fileRepo model.FileRepo fileService model.FileService storage model.Storage defaultAvatar string defaultBackground string defaultHead string } func New( donatRepo model.DonatRepo, widgetRepo model.WidgetRepo, paymentClient model.PaymentClient, authClient model.AuthClient, fileRepo model.FileRepo, fileService model.FileService, storage model.Storage, ) *ServiceDonat { var defaultAvatar = "25b2b94c-e5e6-49e4-9f40-d8814f632847" var defaultBackground = "56527f54-87c0-4f4d-92b5-df6cb61b27d4" var defaultHead = "fef345e0-ec9a-4c68-8f2a-dcccf660e640" return &ServiceDonat{ donatRepo: donatRepo, widgetRepo: widgetRepo, paymentClient: paymentClient, authClient: authClient, fileRepo: fileRepo, fileService: fileService, storage: storage, defaultAvatar: defaultAvatar, defaultBackground: defaultBackground, defaultHead: defaultHead, } } func (donatService *ServiceDonat) CheckToken( request echo.Context, ) (api.CheckTokenResponse, error) { accessToken := model.Token(request.Request().Header.Get("Authorization")) checkTokenResponse, err := donatService.authClient.CheckToken(accessToken) if err != nil { slog.Error("Failed to check token", "error", err.Error()) return api.CheckTokenResponse{}, echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } if checkTokenResponse.AccountID == 0 { slog.Error("Unauthorized account") return api.CheckTokenResponse{}, echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized account") } if checkTokenResponse.AccountID == -1 { slog.Error("Expired token") return api.CheckTokenResponse{}, echo.NewHTTPError(http.StatusUnauthorized, "Expired token") } return checkTokenResponse, nil } func (donatService *ServiceDonat) CreateDonat( ctx context.Context, streamerLogin string, text string, donatUser string, targetID *int, amount int, ) (api.CreatePaymentResponse, error) { donatePage, err := donatService.donatRepo.GetDonatPageByLogin(ctx, streamerLogin) if err != nil { slog.Error("Failed to get donate page", "error", err.Error()) return api.CreatePaymentResponse{}, err } widgets, err := donatService.widgetRepo.GetWidgetsByStreamerID(ctx, donatePage.StreamerID) if err != nil { slog.Error(err.Error()) return api.CreatePaymentResponse{}, err } orderID := uuid.New() var widgetID int for _, widget := range widgets { if amount >= widget.MinAmount && amount <= widget.MaxAmount { widgetID = widget.ID break } } err = donatService.donatRepo.CreateDonat( ctx, donatePage.StreamerID, targetID, widgetID, orderID, amount, text, donatUser, ) if err != nil { slog.Error(err.Error()) return api.CreatePaymentResponse{}, err } createPaymentResponse, err := donatService.paymentClient.CreatePayment(donatePage.StreamerID, amount, orderID) if err != nil { slog.Error(err.Error()) return api.CreatePaymentResponse{}, err } return createPaymentResponse, err } func (donatService *ServiceDonat) GetDonatsByStreamerID( ctx context.Context, streamerID int, page int, limit int, ) ([]*model.Donat, error) { donats, err := donatService.donatRepo.GetDonatsByStreamerID( ctx, streamerID, page, limit, ) if len(donats) == 0 { return []*model.Donat{}, nil } if err != nil { slog.Error(err.Error()) return nil, err } return donats, nil } func (donatService *ServiceDonat) GetDonatByOrderID( ctx context.Context, orderID string, ) (*model.Donat, error) { donat, err := donatService.donatRepo.GetDonatByOrderID(ctx, orderID) if err != nil { slog.Error(err.Error()) return nil, err } return donat, nil } func (donatService *ServiceDonat) MarkDonatPaid( ctx context.Context, orderID string, ) error { err := donatService.donatRepo.MarkDonatPaid( ctx, orderID, ) if err != nil { slog.Error(err.Error()) return err } return nil } func (donatService *ServiceDonat) MarkDonatView( ctx context.Context, orderID string, ) error { err := donatService.donatRepo.MarkDonatView( ctx, orderID, ) if err != nil { slog.Error("donatService.donatRepo.MarkDonatView: " + err.Error()) return err } return nil } func (donatService *ServiceDonat) GetInnerDonatPage( ctx context.Context, streamerID int, ) (model.InnerDonatePageResponse, error) { // Получаем данные страницы доната из репозитория donatePage, err := donatService.donatRepo.GetDonatPage(ctx, streamerID) if err != nil { slog.Error(err.Error()) return model.InnerDonatePageResponse{}, err } // Создаем объекты InnerFile для каждого изображения headImgFile := model.InnerFile{ FileID: donatePage.HeadImgFileId, FileName: donatePage.HeadImgFileName, FileType: donatePage.HeadImgType, FileLink: donatService.storage.DownloadLink(donatePage.HeadImgFileId), FileSize: donatePage.HeadImgSize, } avatarFile := model.InnerFile{ FileID: donatePage.AvatarFileId, FileName: donatePage.AvatarFileName, FileType: donatePage.AvatarType, FileLink: donatService.storage.DownloadLink(donatePage.AvatarFileId), FileSize: donatePage.AvatarImgSize, } backgroundImgFile := model.InnerFile{ FileID: donatePage.BackgroundImgFileId, FileName: donatePage.BackgroundImgFileName, FileType: donatePage.BackgroundImgType, FileLink: donatService.storage.DownloadLink(donatePage.BackgroundImgFileId), FileSize: donatePage.BackgroundImgSize, } // Создаем объект InnerDonatePageResponse innerDonatePageResponse := model.InnerDonatePageResponse{ Description: donatePage.Description, TextAfterDonat: donatePage.TextAfterDonat, ProfileAvatar: donatePage.ProfileAvatar, PageBackground: donatePage.PageBackground, HeadImg: headImgFile, Avatar: avatarFile, BackgroundImg: backgroundImgFile, } return innerDonatePageResponse, nil } func (donatService *ServiceDonat) GetOuterDonatPage( ctx context.Context, streamerLogin string, ) (model.OuterDonatePageResponse, error) { donatePage, err := donatService.donatRepo.GetDonatPageByLogin(ctx, streamerLogin) if err != nil { slog.Error(err.Error()) return model.OuterDonatePageResponse{}, err } var outerDonatePageResponse = model.OuterDonatePageResponse{ Description: donatePage.Description, Login: donatePage.StreamerLogin, OnLine: "online", BackgroundImg: donatService.storage.DownloadLink(donatePage.BackgroundImgFileId), HeadImg: donatService.storage.DownloadLink(donatePage.HeadImgFileId), AvatarImg: donatService.storage.DownloadLink(donatePage.AvatarFileId), } return outerDonatePageResponse, nil } func (donatService *ServiceDonat) UpdateDonatePage( ctx context.Context, streamerID int, updateModel model.UpdateDonatPage, background multipart.FileHeader, headImg multipart.FileHeader, ) error { var backgroundFileID *string var headImgFileID *string if background.Size > 0 { fileID, err := donatService.fileService.AddNewFile( ctx, background, streamerID, "donat_page", ) if err != nil { slog.Error(err.Error()) return err } backgroundFileID = &fileID } if headImg.Size > 0 { fileID, err := donatService.fileService.AddNewFile( ctx, headImg, streamerID, "donat_page", ) if err != nil { slog.Error(err.Error()) return err } headImgFileID = &fileID } err := donatService.donatRepo.UpdateDonatePage( ctx, streamerID, backgroundFileID, headImgFileID, updateModel.Description, updateModel.TextAfterDonat, updateModel.ProfileAvatar, updateModel.PageBackground, ) if err != nil { slog.Error(err.Error()) return err } return nil } func (donatService *ServiceDonat) GetVoiceSettings( ctx context.Context, streamerID int, ) (model.VoiceSettingsResponse, error) { voiceSettings, err := donatService.donatRepo.GetVoiceSettingsByStreamerID(ctx, streamerID) if err != nil { slog.Error("Failed to get voice settings", "error", err) return model.VoiceSettingsResponse{}, err } languages, err := donatService.donatRepo.GetLanguagesByStreamerID(ctx, streamerID) if err != nil { slog.Error("Failed to get languages", "error", err) return model.VoiceSettingsResponse{}, err } // Расширяем структуру VoiceSettingsResponse response := model.VoiceSettingsResponse{ VoiceSpeed: voiceSettings.VoiceSpeed, Scenery: voiceSettings.Scenery, VoiceSoundPercent: voiceSettings.VoiceSoundPercent, MinPrice: voiceSettings.MinPrice, Enable: voiceSettings.Enable, Languages: languages, } return response, nil } func (donatService *ServiceDonat) UpdateVoiceSettings( ctx context.Context, streamerID int, updateModel model.UpdateVoiceSettings, ) error { // Получаем voice_setting_id для streamerID voiceSettingID, err := donatService.donatRepo.GetVoiceSettingIDByStreamerID(ctx, streamerID) if err != nil { slog.Error("Failed to get voice setting ID", "error", err) return err } // Удаляем старые языки err = donatService.donatRepo.DeleteLanguagesByVoiceSettingID(ctx, voiceSettingID) if err != nil { slog.Error("Failed to delete old languages", "error", err) return err } // Получаем ID всех языков по их ISO-кодам languageIDs, err := donatService.donatRepo.GetLanguageIDsByISOCodes(ctx, updateModel.Languages) if err != nil { slog.Error("Failed to get language IDs by ISO codes", "error", err) return err } // Добавляем новые языки (пакетная вставка) err = donatService.donatRepo.InsertLanguagesForVoiceSetting(ctx, voiceSettingID, languageIDs) if err != nil { slog.Error("Failed to insert new languages", "error", err) return err } err = donatService.donatRepo.UpdateVoiceSettings(ctx, streamerID, updateModel) if err != nil { slog.Error("Failed to update voice settings", "error", err) return err } return nil } func (donatService *ServiceDonat) GetFiltersSettings( ctx context.Context, streamerID int, ) (model.FilterSettingResponse, error) { filterSettingID, enableLinks, err := donatService.donatRepo.GetFilterSettingsByStreamerID(ctx, streamerID) if err != nil { slog.Error("Failed to get filter settings", "error", err) return model.FilterSettingResponse{}, err } filteredWords, err := donatService.donatRepo.GetFilteredWords(ctx, filterSettingID) if err != nil { slog.Error("Failed to get filtered words", "error", err) return model.FilterSettingResponse{}, err } if filteredWords == nil { filteredWords = []string{} } response := model.FilterSettingResponse{ EnableLinks: enableLinks, FilteredWords: filteredWords, } return response, nil } func (donatService *ServiceDonat) UpdateFiltersSettings( ctx context.Context, streamerID int, updateModel model.UpdateFilterSettings, ) error { filterID, err := donatService.donatRepo.GetFilterIDByStreamer(ctx, streamerID) if err != nil { slog.Error("Failed to get filter ID", "error", err) return err } if updateModel.EnableLinks != nil { err = donatService.donatRepo.UpdateFilterSettings(ctx, streamerID, updateModel.EnableLinks) if err != nil { slog.Error("Failed to update filter settings", "error", err) return err } } if len(updateModel.AddWords) > 0 { err = donatService.donatRepo.AddFilteredWords(ctx, filterID, updateModel.AddWords) if err != nil { slog.Error("Failed to add filtered words", "error", err) return err } } if len(updateModel.RemoveWords) > 0 { err = donatService.donatRepo.RemoveFilteredWords(ctx, filterID, updateModel.RemoveWords) if err != nil { slog.Error("Failed to remove filtered words", "error", err) return err } } return nil } func (donatService *ServiceDonat) GetModerationSettings( ctx context.Context, streamerID int, ) (model.ModerationResponse, error) { moderationModel, err := donatService.donatRepo.GetModeration( ctx, streamerID, ) if err != nil { slog.Error("Failed to get moderation settings", "error", err) return model.ModerationResponse{}, err } return moderationModel, nil } func (donatService *ServiceDonat) UpdateModerationSettings( ctx context.Context, streamerID int, updateModel model.UpdateModeration, ) error { err := donatService.donatRepo.UpdateModeration( ctx, streamerID, updateModel.Enable, updateModel.Duration, ) if err != nil { slog.Error("Failed to update moderation settings", "error", err) return err } return nil } func (donatService *ServiceDonat) GetDonationsStats( ctx context.Context, streamerID int, period string, ) (model.DonationSummaryResponse, error) { stats, err := donatService.donatRepo.GetDonationsStats(ctx, streamerID, period) if err != nil { slog.Error(err.Error()) return model.DonationSummaryResponse{}, err } var donationsStat []model.DonationStatResponse var response model.DonationSummaryResponse donationSummary, err := donatService.donatRepo.GetDonationsSummary( ctx, streamerID, period, ) if err != nil { slog.Error(err.Error()) return model.DonationSummaryResponse{}, err } response.DonationSum = donationSummary for _, stat := range stats { donationsStat = append(donationsStat, model.DonationStatResponse{ Date: stat.Date, AmountCollected: stat.AmountCollected, DonationsCount: stat.DonationsCount, }) } response.Donations = donationsStat return response, nil } func (donatService *ServiceDonat) GetDonatModeration( ctx context.Context, streamerID int, ) (model.DonationModeration, error) { donation, err := donatService.donatRepo.GetDonatModeration(ctx, streamerID) if err != nil { slog.Error("Failed to get latest donation in moderation", "error", err) return model.DonationModeration{}, err } return donation, nil } func (donatService *ServiceDonat) ModerateDonation( ctx context.Context, donatID int, streamerID int, updateModel model.ModerationDonat, ) error { err := donatService.donatRepo.ModerateDonation( ctx, donatID, streamerID, updateModel, ) if err != nil { slog.Error("Failed to update donat", "error", err) return err } return nil } func (donatService *ServiceDonat) InitNewStreamer( ctx context.Context, streamerID int, login string, ) error { err := donatService.widgetRepo.CreateStreamerWidgetPage( ctx, streamerID, ) if err != nil { slog.Error("Failed to create streamer widget", "error", err) return err } return donatService.donatRepo.InitNewStreamer( ctx, streamerID, login, donatService.defaultAvatar, donatService.defaultBackground, donatService.defaultHead, ) } func (donatService *ServiceDonat) GetPlayingDonat( ctx context.Context, streamerID int, ) (model.PlayingDonat, error) { playingDonat, err := donatService.donatRepo.GetPlayingDonat(ctx, streamerID) if err != nil { slog.Error("Failed to get playing donat", "error", err) return model.PlayingDonat{}, err } if playingDonat.Image != nil { imageUuid, err := uuid.Parse(*playingDonat.Image) if err != nil { return model.PlayingDonat{}, err } fileUrl := donatService.storage.DownloadLink(imageUuid) playingDonat.Image = &fileUrl } if playingDonat.Audio != nil { imageUuid, err := uuid.Parse(*playingDonat.Audio) if err != nil { return model.PlayingDonat{}, err } fileUrl := donatService.storage.DownloadLink(imageUuid) playingDonat.Audio = &fileUrl } return playingDonat, nil } func (donatService *ServiceDonat) UpdateStreamerLogin( ctx context.Context, streamerLogin string, streamerID int, ) error { err := donatService.donatRepo.UpdateStreamerLogin( ctx, streamerLogin, streamerID, ) if err != nil { return err } return nil }