diff --git a/cmd/main.go b/cmd/main.go index acf7038..cfa8b91 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -56,15 +56,17 @@ func main() { // SERVICES widgetService := WidgetService.New(widgetRepo, authClient, fileRepo, storage) + targetService := TargetService.New(targetRepo, authClient) + fileService := FileService.New(authClient, fileRepo, storage) donatService := DonatService.New( donatRepo, widgetRepo, paymentClient, authClient, + fileRepo, + fileService, storage, ) - targetService := TargetService.New(targetRepo, authClient) - fileService := FileService.New(authClient, fileRepo, storage) http.NewApp( db, diff --git a/internal/api/http/handlers/donat/donat.go b/internal/api/http/handlers/donat/donat.go index afff574..5ad0d91 100644 --- a/internal/api/http/handlers/donat/donat.go +++ b/internal/api/http/handlers/donat/donat.go @@ -9,6 +9,7 @@ import ( "github.com/google/uuid" "github.com/labstack/echo/v4" "log/slog" + "mime/multipart" "net/http" "strconv" ) @@ -177,21 +178,21 @@ func GetOuterDonatePage(donatService model.DonatService) echo.HandlerFunc { } // UpdateDonatePage godoc -// @Summary Update personal streamer donate page. -// @Description Update personal streamer donate page. +// @Summary Update personal streamer donate page +// @Description Update personal streamer donate page with optional background and head image files. // @Tags Donate -// @Accept json -// @Produce json +// @Consumes multipart/form-data +// @Produces json // @Security BearerAuth -// @Param request body model.UpdateDonatPage true "Update fields" +// @Param metadata formData model.UpdateDonatPage false "Update fields" // @Param background formData file false "Background image" -// @Param head-img formData file false "Head image" +// @Param head_img formData file false "Head image" // @Success 200 {string} string "Donat page updated successfully" // @Failure 400 {object} echo.HTTPError "Bad request" // @Failure 401 {object} echo.HTTPError "Unauthorized or expired token" // @Failure 422 {object} echo.HTTPError "Validation error" // @Router /donat-page [patch] -func UpdateDonatePage(donatService model.DonatService) echo.HandlerFunc { +func UpdateDonatePage(donatService model.DonatService, fileService model.FileService) echo.HandlerFunc { return func(request echo.Context) error { ctx := context.Background() @@ -203,26 +204,36 @@ func UpdateDonatePage(donatService model.DonatService) echo.HandlerFunc { } authData, err := donatService.CheckToken(request) - //if err != nil { - // slog.Error(err.Error()) - // return echo.NewHTTPError(http.StatusUnauthorized, custom_response.Unauthorized) - //} - - background, err := request.FormFile("background") if err != nil { - background = nil - } - headImg, err := request.FormFile("head_img") - if err != nil { - headImg = nil + slog.Error(err.Error()) + return echo.NewHTTPError(http.StatusUnauthorized, custom_response.Unauthorized) } + bgFile, err := request.FormFile("background") + if err != nil { + bgFile = &multipart.FileHeader{ + Filename: "", + Size: 0, + Header: nil, + } + } + + headImgFile, err := request.FormFile("head_img") + if err != nil { + headImgFile = &multipart.FileHeader{ + Filename: "", + Size: 0, + Header: nil, + } + } + + // Передаем файлы только если они были переданы err = donatService.UpdateDonatePage( ctx, - model.StreamerID(authData.AccountID), + authData.AccountID, body, - background, - headImg, + *bgFile, + *headImgFile, ) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, custom_response.InternalError) diff --git a/internal/app/http/app.go b/internal/app/http/app.go index 375276a..ff6b0ec 100644 --- a/internal/app/http/app.go +++ b/internal/app/http/app.go @@ -46,7 +46,7 @@ func NewApp( server.GET(PREFIX+"/docs/*", echoSwagger.WrapHandler) IncludeWidgetHandlers(server, widgetService) - IncludeDonatHandlers(server, donatService) + IncludeDonatHandlers(server, donatService, fileService) IncludeTargetHandlers(server, targetService, donatService, authClient) IncludeFileHandlers(server, fileService) @@ -67,12 +67,13 @@ func IncludeTargetHandlers( func IncludeDonatHandlers( server *echo.Echo, donatService model.DonatService, + fileService model.FileService, ) { server.POST(PREFIX+"/donat/create", CreateDonat(donatService)) server.GET(PREFIX+"/inner-donate-page", GetInnerDonatePage(donatService)) server.GET(PREFIX+"/outer-donate-page/:streamer-login", GetOuterDonatePage(donatService)) - server.PATCH(PREFIX+"/donat-page", UpdateDonatePage(donatService)) + server.PATCH(PREFIX+"/donat-page", UpdateDonatePage(donatService, fileService)) server.GET(PREFIX+"/donat/get/:streamerID", GetDonat(donatService)) diff --git a/internal/docs/docs.go b/internal/docs/docs.go index 7de6ba9..0214618 100644 --- a/internal/docs/docs.go +++ b/internal/docs/docs.go @@ -22,26 +22,35 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Update personal streamer donate page.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "description": "Update personal streamer donate page with optional background and head image files.", "tags": [ "Donate" ], - "summary": "Update personal streamer donate page.", + "summary": "Update personal streamer donate page", "parameters": [ { - "description": "Update fields", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/donat-widget_internal_model.UpdateDonatPage" - } + "type": "string", + "example": "A great donation page", + "name": "description", + "in": "formData" + }, + { + "type": "string", + "example": "#13161E", + "name": "page_background", + "in": "formData" + }, + { + "type": "boolean", + "example": true, + "name": "profile_avatar", + "in": "formData" + }, + { + "type": "string", + "example": "Thank you for your support!", + "name": "text_after_donat", + "in": "formData" }, { "type": "file", @@ -52,7 +61,7 @@ const docTemplate = `{ { "type": "file", "description": "Head image", - "name": "head-img", + "name": "head_img", "in": "formData" } ], @@ -839,6 +848,9 @@ const docTemplate = `{ "created_at": { "type": "string" }, + "entity": { + "type": "string" + }, "extension": { "type": "string" }, @@ -1020,23 +1032,6 @@ const docTemplate = `{ } } }, - "donat-widget_internal_model.UpdateDonatPage": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "page_background": { - "type": "string" - }, - "profile_avatar": { - "type": "boolean" - }, - "text_after_donat": { - "type": "string" - } - } - }, "donat-widget_internal_model.UpdateFilterSettings": { "type": "object", "properties": { diff --git a/internal/docs/swagger.json b/internal/docs/swagger.json index 49f1cfb..d7780bf 100644 --- a/internal/docs/swagger.json +++ b/internal/docs/swagger.json @@ -15,26 +15,35 @@ "BearerAuth": [] } ], - "description": "Update personal streamer donate page.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + "description": "Update personal streamer donate page with optional background and head image files.", "tags": [ "Donate" ], - "summary": "Update personal streamer donate page.", + "summary": "Update personal streamer donate page", "parameters": [ { - "description": "Update fields", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/donat-widget_internal_model.UpdateDonatPage" - } + "type": "string", + "example": "A great donation page", + "name": "description", + "in": "formData" + }, + { + "type": "string", + "example": "#13161E", + "name": "page_background", + "in": "formData" + }, + { + "type": "boolean", + "example": true, + "name": "profile_avatar", + "in": "formData" + }, + { + "type": "string", + "example": "Thank you for your support!", + "name": "text_after_donat", + "in": "formData" }, { "type": "file", @@ -45,7 +54,7 @@ { "type": "file", "description": "Head image", - "name": "head-img", + "name": "head_img", "in": "formData" } ], @@ -832,6 +841,9 @@ "created_at": { "type": "string" }, + "entity": { + "type": "string" + }, "extension": { "type": "string" }, @@ -1013,23 +1025,6 @@ } } }, - "donat-widget_internal_model.UpdateDonatPage": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "page_background": { - "type": "string" - }, - "profile_avatar": { - "type": "boolean" - }, - "text_after_donat": { - "type": "string" - } - } - }, "donat-widget_internal_model.UpdateFilterSettings": { "type": "object", "properties": { diff --git a/internal/docs/swagger.yaml b/internal/docs/swagger.yaml index 5ae3561..7d10db8 100644 --- a/internal/docs/swagger.yaml +++ b/internal/docs/swagger.yaml @@ -38,6 +38,8 @@ definitions: properties: created_at: type: string + entity: + type: string extension: type: string file_link: @@ -167,17 +169,6 @@ definitions: online: type: string type: object - donat-widget_internal_model.UpdateDonatPage: - properties: - description: - type: string - page_background: - type: string - profile_avatar: - type: boolean - text_after_donat: - type: string - type: object donat-widget_internal_model.UpdateFilterSettings: properties: add_words: @@ -258,26 +249,33 @@ info: paths: /donat-page: patch: - consumes: - - application/json - description: Update personal streamer donate page. + description: Update personal streamer donate page with optional background and + head image files. parameters: - - description: Update fields - in: body - name: request - required: true - schema: - $ref: '#/definitions/donat-widget_internal_model.UpdateDonatPage' + - example: A great donation page + in: formData + name: description + type: string + - example: '#13161E' + in: formData + name: page_background + type: string + - example: true + in: formData + name: profile_avatar + type: boolean + - example: Thank you for your support! + in: formData + name: text_after_donat + type: string - description: Background image in: formData name: background type: file - description: Head image in: formData - name: head-img + name: head_img type: file - produces: - - application/json responses: "200": description: Donat page updated successfully @@ -297,7 +295,7 @@ paths: $ref: '#/definitions/echo.HTTPError' security: - BearerAuth: [] - summary: Update personal streamer donate page. + summary: Update personal streamer donate page tags: - Donate /files: diff --git a/internal/model/interfaces.go b/internal/model/interfaces.go index c14027a..2a5151a 100644 --- a/internal/model/interfaces.go +++ b/internal/model/interfaces.go @@ -85,10 +85,10 @@ type DonatService interface { GetOuterDonatPage(ctx context.Context, streamerLogin string) (OuterDonatePageResponse, error) UpdateDonatePage( ctx context.Context, - streamerID StreamerID, + streamerID int, updateModel UpdateDonatPage, - background *multipart.FileHeader, - headImg *multipart.FileHeader, + background multipart.FileHeader, + headImg multipart.FileHeader, ) error GetVoiceSettings(ctx context.Context, streamerID StreamerID) (VoiceSettingsResponse, error) UpdateVoiceSettings(ctx context.Context, streamerID StreamerID, updateModel UpdateVoiceSettings) error @@ -111,6 +111,15 @@ type DonatRepo interface { GetDonatPage(ctx context.Context, streamerID StreamerID) (DonatePage, error) GetDonatPageByLogin(ctx context.Context, streamerLogin string) (DonatePage, error) + + UpdateDonatePage( + ctx context.Context, + streamerID int, + backgroundFileID *string, + headImgFileID *string, + description *string, + textAfterDonation *string, + ) error } type TargetService interface { diff --git a/internal/model/models.go b/internal/model/models.go index 1fc281d..373f9db 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -157,10 +157,10 @@ type VoiceLanguage struct { } type UpdateDonatPage struct { - ProfileAvatar bool `json:"profile_avatar"` - Description string `json:"description"` - TextAfterDonat string `json:"text_after_donat"` - PageBackground string `json:"page_background"` + ProfileAvatar *bool `json:"profile_avatar,omitempty" form:"profile_avatar" example:"true" description:"Indicates whether the profile avatar is displayed"` + Description *string `json:"description,omitempty" form:"description" example:"A great donation page" description:"Description of the donation page"` + TextAfterDonat *string `json:"text_after_donat,omitempty" form:"text_after_donat" example:"Thank you for your support!" description:"Text displayed after a donation"` + PageBackground *string `json:"page_background,omitempty" form:"page_background" example:"#13161E" description:"Color code"` } type VoiceSettingsResponse struct { diff --git a/internal/model/sql/query.go b/internal/model/sql/query.go index 11b8246..f6bdf01 100644 --- a/internal/model/sql/query.go +++ b/internal/model/sql/query.go @@ -113,9 +113,9 @@ WHERE w.streamer_id = (@streamer_id); ` var AddNewFile = ` -INSERT INTO files (streamer_id, file_name, file_type, extension) +INSERT INTO files (streamer_id, file_name, file_type, extension, entity, size) VALUES -(@streamer_id, @file_name, @file_type, @extension) +(@streamer_id, @file_name, @file_type, @extension, @entity, @size) RETURNING id; ` @@ -157,3 +157,14 @@ WHERE id = @id; var GetWidgetById = ` SELECT id FROM widgets WHERE id = (@widget_id) AND streamer_id = (@streamer_id); ` + +var UpdateDonatePage = ` +UPDATE donate_pages +SET + background_img = COALESCE(@background_img, background_img), + head_img = COALESCE(@head_img, head_img), + description = COALESCE(@description, description), + text_after_donat = COALESCE(@text_after_donation, text_after_donat) +WHERE streamer_id = @streamer_id +RETURNING *; +` diff --git a/internal/repository/donat/donat.go b/internal/repository/donat/donat.go index 465ba53..7ab35f2 100644 --- a/internal/repository/donat/donat.go +++ b/internal/repository/donat/donat.go @@ -184,3 +184,37 @@ func (repoDonat *RepoDonat) GetDonatPageByLogin( return *donatePage[0], nil } + +func (repoDonat *RepoDonat) UpdateDonatePage( + ctx context.Context, + streamerID int, + backgroundFileID *string, + headImgFileID *string, + description *string, + textAfterDonation *string, +) error { + args := pgx.NamedArgs{ + "streamer_id": streamerID, + } + + if backgroundFileID != nil { + args["background_img"] = *backgroundFileID + } + if headImgFileID != nil { + args["head_img"] = *headImgFileID + } + if description != nil { + args["description"] = *description + } + if textAfterDonation != nil { + args["text_after_donat"] = *textAfterDonation + } + + err := repoDonat.db.Update(ctx, sql.UpdateDonatePage, args) + if err != nil { + slog.Error(err.Error()) + return err + } + + return nil +} diff --git a/internal/service/donat/donat.go b/internal/service/donat/donat.go index dcff8dc..7ea5227 100644 --- a/internal/service/donat/donat.go +++ b/internal/service/donat/donat.go @@ -15,6 +15,8 @@ type ServiceDonat struct { widgetRepo model.WidgetRepo authClient model.AuthClient paymentClient model.PaymentClient + fileRepo model.FileRepo + fileService model.FileService storage model.Storage } @@ -23,6 +25,8 @@ func New( widgetRepo model.WidgetRepo, paymentClient model.PaymentClient, authClient model.AuthClient, + fileRepo model.FileRepo, + fileService model.FileService, storage model.Storage, ) *ServiceDonat { return &ServiceDonat{ @@ -30,6 +34,8 @@ func New( widgetRepo: widgetRepo, paymentClient: paymentClient, authClient: authClient, + fileRepo: fileRepo, + fileService: fileService, storage: storage, } } @@ -196,14 +202,55 @@ func (donatService *ServiceDonat) GetOuterDonatPage( func (donatService *ServiceDonat) UpdateDonatePage( ctx context.Context, - streamerID model.StreamerID, + streamerID int, updateModel model.UpdateDonatPage, - background *multipart.FileHeader, - headImg *multipart.FileHeader, + 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, + ) + if err != nil { + slog.Error(err.Error()) + return err + } return nil - } func (donatService *ServiceDonat) GetVoiceSettings( diff --git a/sql/init.sql b/sql/init.sql index da07ece..faafeec 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -9,6 +9,7 @@ CREATE TABLE IF NOT EXISTS files ( extension VARCHAR(10) NOT NULL, streamer_id INTEGER NOT NULL, entity VARCHAR(50) NOT NULL DEFAULT 'widget', + size FLOAT NOT NULL DEFAULT 10, created_at TIMESTAMP DEFAULT now() ); CREATE TABLE IF NOT EXISTS widgets (