diff --git a/internal/api/http/handlers/donat/donat.go b/internal/api/http/handlers/donat/donat.go index bebd908..e64c4f4 100644 --- a/internal/api/http/handlers/donat/donat.go +++ b/internal/api/http/handlers/donat/donat.go @@ -3,10 +3,10 @@ package donat import ( "context" "donat-widget/internal/model" + _ "donat-widget/internal/model/api" "donat-widget/pkg/custom_response" "donat-widget/pkg/validator" "fmt" - "github.com/google/uuid" "github.com/labstack/echo/v4" "log/slog" "mime/multipart" @@ -14,36 +14,39 @@ import ( "strconv" ) +// CreateDonat godoc +// @Summary Create donat +// @Description Create donat +// @Tags Donate +// @Accept json +// @Produce json +// @Param streamer-login path string true "Login стримера" +// @Param request body model.CreateDonatBody true "Create donat body json" +// @Success 200 {object} api.CreatePaymentResponse "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/{streamer-login} [post] func CreateDonat(donatService model.DonatService) echo.HandlerFunc { - type CreateDonatBody struct { - StreamerID model.StreamerID `json:"streamerID"` - TargetID model.TargetID `json:"targetID"` - Amount model.DonatAmount `json:"amount"` - Text string `json:"text"` - DonatUser string `json:"donatUser"` - } return func(request echo.Context) error { ctx := context.Background() - var body CreateDonatBody - if err := request.Bind(&body); err != nil { + var body model.CreateDonatBody + + err := validator.ParseAndValidate(&body, request) + if err != nil { slog.Error(err.Error()) - return request.JSON(http.StatusInternalServerError, err.Error()) - } - if err := request.Validate(&body); err != nil { - slog.Error(err.Error()) - return request.JSON(http.StatusInternalServerError, err.Error()) + return echo.NewHTTPError(http.StatusUnprocessableEntity, "Unprocessable Entity") } - orderID := model.OrderID(uuid.New().String()) + streamerLogin := request.Param("streamer-login") order, err := donatService.CreateDonat( ctx, - body.StreamerID, - orderID, - body.TargetID, - body.Amount, + streamerLogin, body.Text, body.DonatUser, + body.TargetID, + body.Amount, ) if err != nil { return request.JSON(http.StatusInternalServerError, err.Error()) diff --git a/internal/api/http/handlers/widget/widget.go b/internal/api/http/handlers/widget/widget.go index 49da468..fbafb99 100644 --- a/internal/api/http/handlers/widget/widget.go +++ b/internal/api/http/handlers/widget/widget.go @@ -247,7 +247,7 @@ func UpdateWidget(widgetService model.WidgetService) echo.HandlerFunc { // slog.Info("set " + mediaType + " file successfully") // return request.String(200, "File successfully uploaded") // } -//} +//}e // //func GetMediaFile(widgetService model.WidgetService) echo.HandlerFunc { // return func(request echo.Context) error { diff --git a/internal/app/http/app.go b/internal/app/http/app.go index 1f1fff9..4cecb15 100644 --- a/internal/app/http/app.go +++ b/internal/app/http/app.go @@ -69,7 +69,7 @@ func IncludeDonatHandlers( donatService model.DonatService, fileService model.FileService, ) { - server.POST(PREFIX+"/donat/create", CreateDonat(donatService)) + server.POST(PREFIX+"/donat/:streamer-login", CreateDonat(donatService)) server.GET(PREFIX+"/inner-donate-page", GetInnerDonatePage(donatService)) server.GET(PREFIX+"/outer-donate-page/:streamer-login", GetOuterDonatePage(donatService)) diff --git a/internal/docs/docs.go b/internal/docs/docs.go index d353710..273fbd0 100644 --- a/internal/docs/docs.go +++ b/internal/docs/docs.go @@ -93,6 +93,65 @@ const docTemplate = `{ } } }, + "/donat/{streamer-login}": { + "post": { + "description": "Create donat", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Donate" + ], + "summary": "Create donat", + "parameters": [ + { + "type": "string", + "description": "Login стримера", + "name": "streamer-login", + "in": "path", + "required": true + }, + { + "description": "Create donat body json", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/donat-widget_internal_model.CreateDonatBody" + } + } + ], + "responses": { + "200": { + "description": "Donat page updated successfully", + "schema": { + "$ref": "#/definitions/donat-widget_internal_model_api.CreatePaymentResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "401": { + "description": "Unauthorized or expired token", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "422": { + "description": "Validation error", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + } + } + } + }, "/files": { "post": { "security": [ @@ -827,6 +886,23 @@ const docTemplate = `{ } }, "definitions": { + "donat-widget_internal_model.CreateDonatBody": { + "type": "object", + "properties": { + "amount": { + "type": "integer" + }, + "donatUser": { + "type": "string" + }, + "targetID": { + "type": "integer" + }, + "text": { + "type": "string" + } + } + }, "donat-widget_internal_model.CreateWidgetBody": { "type": "object", "required": [ @@ -990,6 +1066,11 @@ const docTemplate = `{ "format": "int64", "example": 100 }, + "name": { + "type": "string", + "format": "string", + "example": "Мой виджет 10" + }, "streamer_id": { "type": "integer", "format": "int64", @@ -1237,6 +1318,35 @@ const docTemplate = `{ } } }, + "donat-widget_internal_model_api.CreatePaymentResponse": { + "type": "object", + "properties": { + "Amount": { + "type": "integer" + }, + "ErrorCode": { + "type": "string" + }, + "OrderId": { + "type": "string" + }, + "PaymentId": { + "type": "string" + }, + "PaymentURL": { + "type": "string" + }, + "Status": { + "type": "string" + }, + "Success": { + "type": "boolean" + }, + "TerminalKey": { + "type": "string" + } + } + }, "echo.HTTPError": { "type": "object", "properties": { diff --git a/internal/docs/swagger.json b/internal/docs/swagger.json index 3a5b167..67f2eb6 100644 --- a/internal/docs/swagger.json +++ b/internal/docs/swagger.json @@ -86,6 +86,65 @@ } } }, + "/donat/{streamer-login}": { + "post": { + "description": "Create donat", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Donate" + ], + "summary": "Create donat", + "parameters": [ + { + "type": "string", + "description": "Login стримера", + "name": "streamer-login", + "in": "path", + "required": true + }, + { + "description": "Create donat body json", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/donat-widget_internal_model.CreateDonatBody" + } + } + ], + "responses": { + "200": { + "description": "Donat page updated successfully", + "schema": { + "$ref": "#/definitions/donat-widget_internal_model_api.CreatePaymentResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "401": { + "description": "Unauthorized or expired token", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "422": { + "description": "Validation error", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + } + } + } + }, "/files": { "post": { "security": [ @@ -820,6 +879,23 @@ } }, "definitions": { + "donat-widget_internal_model.CreateDonatBody": { + "type": "object", + "properties": { + "amount": { + "type": "integer" + }, + "donatUser": { + "type": "string" + }, + "targetID": { + "type": "integer" + }, + "text": { + "type": "string" + } + } + }, "donat-widget_internal_model.CreateWidgetBody": { "type": "object", "required": [ @@ -983,6 +1059,11 @@ "format": "int64", "example": 100 }, + "name": { + "type": "string", + "format": "string", + "example": "Мой виджет 10" + }, "streamer_id": { "type": "integer", "format": "int64", @@ -1230,6 +1311,35 @@ } } }, + "donat-widget_internal_model_api.CreatePaymentResponse": { + "type": "object", + "properties": { + "Amount": { + "type": "integer" + }, + "ErrorCode": { + "type": "string" + }, + "OrderId": { + "type": "string" + }, + "PaymentId": { + "type": "string" + }, + "PaymentURL": { + "type": "string" + }, + "Status": { + "type": "string" + }, + "Success": { + "type": "boolean" + }, + "TerminalKey": { + "type": "string" + } + } + }, "echo.HTTPError": { "type": "object", "properties": { diff --git a/internal/docs/swagger.yaml b/internal/docs/swagger.yaml index 26ea577..ca94c0e 100644 --- a/internal/docs/swagger.yaml +++ b/internal/docs/swagger.yaml @@ -1,5 +1,16 @@ basePath: /api/widget definitions: + donat-widget_internal_model.CreateDonatBody: + properties: + amount: + type: integer + donatUser: + type: string + targetID: + type: integer + text: + type: string + type: object donat-widget_internal_model.CreateWidgetBody: properties: audio: @@ -121,6 +132,10 @@ definitions: example: 100 format: int64 type: integer + name: + example: Мой виджет 10 + format: string + type: string streamer_id: example: 1001 format: int64 @@ -290,6 +305,25 @@ definitions: $ref: '#/definitions/donat-widget_internal_model.GetWidgetDb' type: array type: object + donat-widget_internal_model_api.CreatePaymentResponse: + properties: + Amount: + type: integer + ErrorCode: + type: string + OrderId: + type: string + PaymentId: + type: string + PaymentURL: + type: string + Status: + type: string + Success: + type: boolean + TerminalKey: + type: string + type: object echo.HTTPError: properties: message: {} @@ -351,6 +385,45 @@ paths: summary: Update personal streamer donate page tags: - Donate + /donat/{streamer-login}: + post: + consumes: + - application/json + description: Create donat + parameters: + - description: Login стримера + in: path + name: streamer-login + required: true + type: string + - description: Create donat body json + in: body + name: request + required: true + schema: + $ref: '#/definitions/donat-widget_internal_model.CreateDonatBody' + produces: + - application/json + responses: + "200": + description: Donat page updated successfully + schema: + $ref: '#/definitions/donat-widget_internal_model_api.CreatePaymentResponse' + "400": + description: Bad request + schema: + $ref: '#/definitions/echo.HTTPError' + "401": + description: Unauthorized or expired token + schema: + $ref: '#/definitions/echo.HTTPError' + "422": + description: Validation error + schema: + $ref: '#/definitions/echo.HTTPError' + summary: Create donat + tags: + - Donate /files: post: consumes: diff --git a/internal/model/interfaces.go b/internal/model/interfaces.go index 91fb499..1997360 100644 --- a/internal/model/interfaces.go +++ b/internal/model/interfaces.go @@ -73,7 +73,7 @@ type WidgetRepo interface { type DonatService interface { CheckToken(request echo.Context) (api.CheckTokenResponse, error) - CreateDonat(ctx context.Context, streamerID StreamerID, orderID OrderID, targetID TargetID, amount DonatAmount, text string, donatUser string) (api.CreatePaymentResponse, error) + CreateDonat(ctx context.Context, streamerLogin, text, donatUser string, targetID, amount int) (api.CreatePaymentResponse, error) GetDonatByStreamerID(ctx context.Context, streamerID StreamerID) ([]*Donat, error) GetDonatByOrderID(ctx context.Context, orderID OrderID) (*Donat, error) @@ -101,7 +101,16 @@ type DonatService interface { } type DonatRepo interface { - CreateDonat(ctx context.Context, streamerID StreamerID, widgetID WidgetID, orderID OrderID, targetID TargetID, amount DonatAmount, text string, donatUser string) error + CreateDonat( + ctx context.Context, + streamerID int, + targetID int, + widgetID int, + orderID uuid.UUID, + amount int, + text string, + donatUser string, + ) error GetDonatByStreamerID(ctx context.Context, streamerID StreamerID) ([]*Donat, error) GetDonatByOrderID(ctx context.Context, orderID OrderID) (*Donat, error) @@ -166,7 +175,7 @@ type Storage interface { } type PaymentClient interface { - CreatePayment(streamerID StreamerID, amount DonatAmount, orderID OrderID) (api.CreatePaymentResponse, error) + CreatePayment(streamerID int, amount int, orderID uuid.UUID) (api.CreatePaymentResponse, error) } type Db interface { Insert(ctx context.Context, query string, args ...interface{}) (any, error) diff --git a/internal/model/models.go b/internal/model/models.go index aa8a27a..a03f353 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -21,6 +21,7 @@ type GetWidgetDb struct { ID int `db:"id" json:"id" example:"1" format:"int64" description:"Unique identifier of the widget"` StreamerID int `db:"streamer_id" json:"streamer_id" example:"1001" format:"int64" description:"ID of the streamer"` TemplateID int `db:"template_id" json:"template_id" example:"5" format:"int64" description:"ID of the template"` + Name string `db:"name" json:"name" example:"Мой виджет 10" format:"string" description:"Имя виджета"` Duration int `db:"duration" json:"duration" example:"30" format:"int64" description:"Duration of the widget"` MinAmount int `db:"min_amount" json:"min_amount" example:"100" format:"int64" description:"Minimum donation amount"` MaxAmount int `db:"max_amount" json:"max_amount" example:"1000" format:"int64" description:"Maximum donation amount"` @@ -68,8 +69,7 @@ type Donat struct { DonatUser string `db:"donat_user"` Amount DonatAmount `db:"amount"` - Paid bool `db:"paid"` - View bool `db:"view"` + status string `db:"status"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` @@ -248,6 +248,13 @@ type DonatAndWidget struct { Display Display } +type CreateDonatBody struct { + TargetID int `json:"targetID"` + Amount int `json:"amount"` + Text string `json:"text"` + DonatUser string `json:"donatUser"` +} + //func (widget *GetWidgetDb) GetMediaUrl(mediaType MediaType) MediaUrl { // var mediaUrl MediaUrl // if mediaType == "background_url" { diff --git a/internal/model/sql/model.go b/internal/model/sql/model.go index 0ae6634..7cc2ced 100644 --- a/internal/model/sql/model.go +++ b/internal/model/sql/model.go @@ -34,18 +34,16 @@ CREATE TABLE IF NOT EXISTS files ( CREATE TABLE IF NOT EXISTS donats ( id SERIAL PRIMARY KEY, streamer_id INTEGER NOT NULL, - widget_id INTEGER NOT NULL, - order_id TEXT NOT NULL, - target_id INTEGER NOT NULL, - size FLOAT NOT NULL, + widget_id INTEGER REFERENCES widgets(id) NOT NULL, + order_id UUID NOT NULL, + target_id INTEGER, + + status VARCHAR(50) NOT NULL DEFAULT 'pending', text TEXT NOT NULL, amount INTEGER NOT NULL, donat_user TEXT NOT NULL, - - paid BOOLEAN DEFAULT FALSE, - view BOOLEAN DEFAULT FALSE, - + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); diff --git a/internal/model/sql/query.go b/internal/model/sql/query.go index 03359ed..066a615 100644 --- a/internal/model/sql/query.go +++ b/internal/model/sql/query.go @@ -155,6 +155,7 @@ SELECT w.streamer_id, w.template_id, w.group_id, + w.name, img.id AS image_id, img.file_name AS image_file_name, img.file_type AS image_type, diff --git a/internal/repository/donat/donat.go b/internal/repository/donat/donat.go index 27f26c9..d0213ce 100644 --- a/internal/repository/donat/donat.go +++ b/internal/repository/donat/donat.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "github.com/georgysavva/scany/v2/pgxscan" + "github.com/google/uuid" "github.com/jackc/pgx/v5" "log/slog" "strings" @@ -24,11 +25,11 @@ type RepoDonat struct { func (repoDonat *RepoDonat) CreateDonat( ctx context.Context, - streamerID model.StreamerID, - widgetID model.WidgetID, - orderID model.OrderID, - targetID model.TargetID, - amount model.DonatAmount, + streamerID int, + targetID int, + widgetID int, + orderID uuid.UUID, + amount int, text string, donatUser string, ) error { @@ -39,6 +40,7 @@ func (repoDonat *RepoDonat) CreateDonat( "target_id": targetID, "text": text, "amount": amount, + "status": "pending", "donat_user": donatUser, } _, err := repoDonat.db.Insert(ctx, sql.CreateDonat, args) diff --git a/internal/service/donat/donat.go b/internal/service/donat/donat.go index 087bc20..a066ade 100644 --- a/internal/service/donat/donat.go +++ b/internal/service/donat/donat.go @@ -4,6 +4,7 @@ import ( "context" "donat-widget/internal/model" "donat-widget/internal/model/api" + "github.com/google/uuid" "github.com/labstack/echo/v4" "log/slog" "mime/multipart" @@ -66,42 +67,48 @@ func (donatService *ServiceDonat) CheckToken( func (donatService *ServiceDonat) CreateDonat( ctx context.Context, - streamerID model.StreamerID, - orderID model.OrderID, - targetID model.TargetID, - amount model.DonatAmount, + streamerLogin string, text string, donatUser string, + targetID int, + amount int, ) (api.CreatePaymentResponse, error) { - return api.CreatePaymentResponse{}, nil - //widgets, err := donatService.widgetRepo.GetAllWidget(ctx, streamerID) - //if err != nil { - // slog.Error(err.Error()) - // return api.CreatePaymentResponse{}, err - //} - // - //var widgetID model.WidgetID - //for _, widget := range widgets { - // if widget.MinAmount <= amount { - // widgetID = widget.ID - // } - //} - // - //err = donatService.donatRepo.CreateDonat( - // ctx, streamerID, widgetID, orderID, targetID, amount, text, donatUser, - //) - //if err != nil { - // slog.Error(err.Error()) - // return api.CreatePaymentResponse{}, err - //} - // + 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 widget.MinAmount <= amount && widget.MaxAmount <= amount { + widgetID = widget.ID + } + } + + 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(streamerID, amount, orderID) //if err != nil { // slog.Error(err.Error()) // return api.CreatePaymentResponse{}, err //} - // - //return createPaymentResponse, nil + + return api.CreatePaymentResponse{}, err } func (donatService *ServiceDonat) GetDonatByStreamerID( diff --git a/pkg/api/payment/tinkoff.go b/pkg/api/payment/tinkoff.go index f4c9235..c16ebcb 100644 --- a/pkg/api/payment/tinkoff.go +++ b/pkg/api/payment/tinkoff.go @@ -2,10 +2,10 @@ package payment import ( "bytes" - "donat-widget/internal/model" "donat-widget/internal/model/api" "encoding/json" "errors" + "github.com/google/uuid" "io" "log/slog" "net/http" @@ -24,9 +24,9 @@ type ClientPayment struct { } func (c *ClientPayment) CreatePayment( - streamerID model.StreamerID, - amount model.DonatAmount, - orderID model.OrderID, + streamerID int, + amount int, + orderID uuid.UUID, ) (api.CreatePaymentResponse, error) { requestBody := map[string]any{ "sellerID": streamerID, diff --git a/sql/init.sql b/sql/init.sql index faafeec..10da714 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -42,8 +42,7 @@ CREATE TABLE IF NOT EXISTS donats ( text TEXT NOT NULL, amount INTEGER NOT NULL, donat_user TEXT NOT NULL, - paid BOOLEAN DEFAULT FALSE, - view BOOLEAN DEFAULT FALSE, + status VARCHAR(50) NOT NULL DEFAULT 'pending', created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP );