diff --git a/internal/api/http/handlers/donat/donat.go b/internal/api/http/handlers/donat/donat.go index 7a8c426..78018b7 100644 --- a/internal/api/http/handlers/donat/donat.go +++ b/internal/api/http/handlers/donat/donat.go @@ -491,3 +491,38 @@ func GetDonationsStats(donatService model.DonatService) echo.HandlerFunc { return request.JSON(http.StatusOK, stats) } } + +// DonateModeration godoc +// @Summary Get the latest donation in moderation +// @Description Get the latest donation with status 'moderation' sorted by updated_at +// @Tags Donate +// @Accept json +// @Produce json +// @Security BearerAuth +// @Success 200 {object} model.DonationModeration "Latest donation in moderation" +// @Failure 400 {object} echo.HTTPError "Bad request" +// @Failure 401 {object} echo.HTTPError "Unauthorized or expired token" +// @Failure 404 {object} echo.HTTPError "No donations in moderation found" +// @Failure 500 {object} echo.HTTPError "Internal server error" +// @Router /donat/latest-moderation [get] +func DonateModeration(donatService model.DonatService) echo.HandlerFunc { + return func(request echo.Context) error { + authData, err := donatService.CheckToken(request) + if err != nil { + slog.Error("Unauthorized") + return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) + } + ctx := context.Background() + + donation, err := donatService.GetDonatModeration(ctx, authData.AccountID) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + if donation.ID == 0 { // Если донат не найден + return request.JSON(http.StatusOK, map[string]interface{}{}) + } + + return request.JSON(http.StatusOK, donation) + } +} diff --git a/internal/app/http/app.go b/internal/app/http/app.go index 57acfcd..e8bebc5 100644 --- a/internal/app/http/app.go +++ b/internal/app/http/app.go @@ -90,6 +90,8 @@ func IncludeDonatHandlers( server.POST(PREFIX+"/donat/paid", MarkDonatPaid(donatService)) server.GET(PREFIX+"/donat/period-stat", GetDonationsStats(donatService)) + + server.GET(PREFIX+"/donat/latest-moderation", DonateModeration(donatService)) } func IncludeWidgetHandlers( diff --git a/internal/docs/docs.go b/internal/docs/docs.go index 5ee0381..3acc17a 100644 --- a/internal/docs/docs.go +++ b/internal/docs/docs.go @@ -93,6 +93,58 @@ const docTemplate = `{ } } }, + "/donat/latest-moderation": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the latest donation with status 'moderation' sorted by updated_at", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Donate" + ], + "summary": "Get the latest donation in moderation", + "responses": { + "200": { + "description": "Latest donation in moderation", + "schema": { + "$ref": "#/definitions/donat-widget_internal_model.DonationModeration" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "401": { + "description": "Unauthorized or expired token", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "404": { + "description": "No donations in moderation found", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + } + } + } + }, "/donat/period-stat": { "get": { "security": [ @@ -1047,6 +1099,71 @@ const docTemplate = `{ } } }, + "donat-widget_internal_model.DonationModeration": { + "type": "object", + "properties": { + "accepted": { + "description": "Донат принят на модерации", + "type": "boolean", + "example": false + }, + "amount": { + "description": "Сумма доната", + "type": "integer", + "example": 2000 + }, + "created_at": { + "description": "Дата создания", + "type": "string", + "example": "2025-03-09T12:49:21.75005Z" + }, + "donat_user": { + "description": "Имя пользователя, сделавшего донат", + "type": "string", + "example": "Петр" + }, + "id": { + "description": "Уникальный идентификатор доната", + "type": "integer", + "example": 123 + }, + "play_content": { + "description": "Проигрывать контент", + "type": "boolean", + "example": true + }, + "show_name": { + "description": "Показывать имя пользователя", + "type": "boolean", + "example": true + }, + "show_text": { + "description": "Показывать текст доната", + "type": "boolean", + "example": true + }, + "status": { + "description": "Статус доната (moderation, pending, viewed и т.д.)", + "type": "string", + "example": "moderation" + }, + "target_id": { + "description": "ID цели (если есть)", + "type": "integer", + "example": 0 + }, + "text": { + "description": "Текст доната", + "type": "string", + "example": "Пиздатый стрим" + }, + "updated_at": { + "description": "Дата последнего обновления", + "type": "string", + "example": "2025-03-09T12:49:21.75005Z" + } + } + }, "donat-widget_internal_model.DonationStatResponse": { "type": "object", "properties": { diff --git a/internal/docs/swagger.json b/internal/docs/swagger.json index cde5262..5484f99 100644 --- a/internal/docs/swagger.json +++ b/internal/docs/swagger.json @@ -86,6 +86,58 @@ } } }, + "/donat/latest-moderation": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the latest donation with status 'moderation' sorted by updated_at", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Donate" + ], + "summary": "Get the latest donation in moderation", + "responses": { + "200": { + "description": "Latest donation in moderation", + "schema": { + "$ref": "#/definitions/donat-widget_internal_model.DonationModeration" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "401": { + "description": "Unauthorized or expired token", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "404": { + "description": "No donations in moderation found", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + } + } + } + }, "/donat/period-stat": { "get": { "security": [ @@ -1040,6 +1092,71 @@ } } }, + "donat-widget_internal_model.DonationModeration": { + "type": "object", + "properties": { + "accepted": { + "description": "Донат принят на модерации", + "type": "boolean", + "example": false + }, + "amount": { + "description": "Сумма доната", + "type": "integer", + "example": 2000 + }, + "created_at": { + "description": "Дата создания", + "type": "string", + "example": "2025-03-09T12:49:21.75005Z" + }, + "donat_user": { + "description": "Имя пользователя, сделавшего донат", + "type": "string", + "example": "Петр" + }, + "id": { + "description": "Уникальный идентификатор доната", + "type": "integer", + "example": 123 + }, + "play_content": { + "description": "Проигрывать контент", + "type": "boolean", + "example": true + }, + "show_name": { + "description": "Показывать имя пользователя", + "type": "boolean", + "example": true + }, + "show_text": { + "description": "Показывать текст доната", + "type": "boolean", + "example": true + }, + "status": { + "description": "Статус доната (moderation, pending, viewed и т.д.)", + "type": "string", + "example": "moderation" + }, + "target_id": { + "description": "ID цели (если есть)", + "type": "integer", + "example": 0 + }, + "text": { + "description": "Текст доната", + "type": "string", + "example": "Пиздатый стрим" + }, + "updated_at": { + "description": "Дата последнего обновления", + "type": "string", + "example": "2025-03-09T12:49:21.75005Z" + } + } + }, "donat-widget_internal_model.DonationStatResponse": { "type": "object", "properties": { diff --git a/internal/docs/swagger.yaml b/internal/docs/swagger.yaml index c1a03be..d4dcca6 100644 --- a/internal/docs/swagger.yaml +++ b/internal/docs/swagger.yaml @@ -69,6 +69,57 @@ definitions: streamer_id: type: integer type: object + donat-widget_internal_model.DonationModeration: + properties: + accepted: + description: Донат принят на модерации + example: false + type: boolean + amount: + description: Сумма доната + example: 2000 + type: integer + created_at: + description: Дата создания + example: "2025-03-09T12:49:21.75005Z" + type: string + donat_user: + description: Имя пользователя, сделавшего донат + example: Петр + type: string + id: + description: Уникальный идентификатор доната + example: 123 + type: integer + play_content: + description: Проигрывать контент + example: true + type: boolean + show_name: + description: Показывать имя пользователя + example: true + type: boolean + show_text: + description: Показывать текст доната + example: true + type: boolean + status: + description: Статус доната (moderation, pending, viewed и т.д.) + example: moderation + type: string + target_id: + description: ID цели (если есть) + example: 0 + type: integer + text: + description: Текст доната + example: Пиздатый стрим + type: string + updated_at: + description: Дата последнего обновления + example: "2025-03-09T12:49:21.75005Z" + type: string + type: object donat-widget_internal_model.DonationStatResponse: properties: amount_collected: @@ -454,6 +505,39 @@ paths: summary: Create donat tags: - Donate + /donat/latest-moderation: + get: + consumes: + - application/json + description: Get the latest donation with status 'moderation' sorted by updated_at + produces: + - application/json + responses: + "200": + description: Latest donation in moderation + schema: + $ref: '#/definitions/donat-widget_internal_model.DonationModeration' + "400": + description: Bad request + schema: + $ref: '#/definitions/echo.HTTPError' + "401": + description: Unauthorized or expired token + schema: + $ref: '#/definitions/echo.HTTPError' + "404": + description: No donations in moderation found + schema: + $ref: '#/definitions/echo.HTTPError' + "500": + description: Internal server error + schema: + $ref: '#/definitions/echo.HTTPError' + security: + - BearerAuth: [] + summary: Get the latest donation in moderation + tags: + - Donate /donat/period-stat: get: consumes: diff --git a/internal/model/interfaces.go b/internal/model/interfaces.go index 639bbba..3d51b31 100644 --- a/internal/model/interfaces.go +++ b/internal/model/interfaces.go @@ -102,6 +102,8 @@ type DonatService interface { UpdateModerationSettings(ctx context.Context, streamerID int, updateModel UpdateModeration) error GetDonationsStats(ctx context.Context, streamerID int, period string) (DonationSummaryResponse, error) + + GetDonatModeration(ctx context.Context, streamerID int) (DonationModeration, error) } type DonatRepo interface { @@ -154,6 +156,8 @@ type DonatRepo interface { GetDonationsStats(ctx context.Context, streamerID int, period string) ([]DonationStat, error) GetDonationsSummary(ctx context.Context, streamerID int, period string) (DonationSummary, error) + + GetDonatModeration(ctx context.Context, streamerID int) (DonationModeration, error) } type TargetService interface { diff --git a/internal/model/models.go b/internal/model/models.go index e62ddd3..2b5a616 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -278,6 +278,21 @@ type DonationSummaryResponse struct { Donations []DonationStatResponse `json:"donations"` } +type DonationModeration struct { + ID int `db:"id" json:"id" example:"123"` // Уникальный идентификатор доната + TargetID int `db:"target_id" json:"target_id" example:"0"` // ID цели (если есть) + Status string `db:"status" json:"status" example:"moderation"` // Статус доната (moderation, pending, viewed и т.д.) + Text string `db:"text" json:"text" example:"Пиздатый стрим"` // Текст доната + Amount int `db:"amount" json:"amount" example:"2000"` // Сумма доната + DonatUser string `db:"donat_user" json:"donat_user" example:"Петр"` // Имя пользователя, сделавшего донат + Accepted bool `db:"accepted" json:"accepted" example:"false"` // Донат принят на модерации + ShowName bool `db:"show_name" json:"show_name" example:"true"` // Показывать имя пользователя + ShowText bool `db:"show_text" json:"show_text" example:"true"` // Показывать текст доната + PlayContent bool `db:"play_content" json:"play_content" example:"true"` // Проигрывать контент + CreatedAt time.Time `db:"created_at" json:"created_at" example:"2025-03-09T12:49:21.75005Z"` // Дата создания + UpdatedAt time.Time `db:"updated_at" json:"updated_at" example:"2025-03-09T12:49:21.75005Z"` // Дата последнего обновления +} + //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 7cc2ced..17a8294 100644 --- a/internal/model/sql/model.go +++ b/internal/model/sql/model.go @@ -43,7 +43,14 @@ CREATE TABLE IF NOT EXISTS donats ( text TEXT NOT NULL, amount INTEGER NOT NULL, donat_user TEXT NOT NULL, - + + accepted BOOLEAN NOT NULL DEFAULT 'false', + show_name BOOLEAN NOT NULL DEFAULT 'true', + show_text BOOLEAN NOT NULL DEFAULT 'true', + play_content BOOLEAN NOT NULL DEFAULT 'true', + + showed BOOLEAN NOT NULL 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 4891968..9561ecb 100644 --- a/internal/model/sql/query.go +++ b/internal/model/sql/query.go @@ -421,3 +421,27 @@ WHERE streamer_id = @streamer_id AND created_at >= NOW() - INTERVAL '1 year'; ` + +const GetLastModeration = ` +SELECT + id, + target_id, + status, + text, + amount, + donat_user, + accepted, + show_name, + show_text, + play_content, + created_at, + updated_at +FROM + public.donats +WHERE + status = 'moderation' AND + streamer_id = @streamer_id +ORDER BY + updated_at DESC +LIMIT 1; +` diff --git a/internal/repository/donat/donat.go b/internal/repository/donat/donat.go index ed6eca0..1025f2c 100644 --- a/internal/repository/donat/donat.go +++ b/internal/repository/donat/donat.go @@ -681,3 +681,41 @@ func (repoDonat *RepoDonat) GetDonationsSummary( return summary, nil } + +func (repoDonat *RepoDonat) GetDonatModeration( + ctx context.Context, + streamerID int, +) (model.DonationModeration, error) { + var donation model.DonationModeration + args := pgx.NamedArgs{ + "streamer_id": streamerID, + } + row, err := repoDonat.db.SelectOne(ctx, sql.GetLastModeration, args) + if err != nil { + slog.Error("Failed to get donation", "error", err) + return model.DonationModeration{}, err + } + if row == nil { + return model.DonationModeration{}, nil + } + err = row.Scan( + &donation.ID, + &donation.TargetID, + &donation.Status, + &donation.Text, + &donation.Amount, + &donation.DonatUser, + &donation.Accepted, + &donation.ShowName, + &donation.ShowText, + &donation.PlayContent, + &donation.CreatedAt, + &donation.UpdatedAt, + ) + if err != nil { + slog.Error("Failed to get latest donation in moderation", "error", err) + return model.DonationModeration{}, err + } + + return donation, nil +} diff --git a/internal/service/donat/donat.go b/internal/service/donat/donat.go index c97c9d7..f66903b 100644 --- a/internal/service/donat/donat.go +++ b/internal/service/donat/donat.go @@ -492,3 +492,16 @@ func (donatService *ServiceDonat) GetDonationsStats( 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 +}