This commit is contained in:
mm 2024-10-20 22:42:26 +05:00
parent b94abfe75b
commit 13a80221ed
31 changed files with 912 additions and 798 deletions

View File

@ -22,23 +22,23 @@ jobs:
dockerhub-st.ru/donat-widget:latest
dockerhub-st.ru/donat-widget:${{ github.sha }}
# deploy:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v2
#
# - name: Install kubectl
# uses: azure/setup-kubectl@v1
# with:
# version: 'v1.21.0'
#
# - name: Configure kube-config
# run: |
# mkdir -p $HOME/.kube
# echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config
# export KUBECONFIG=$HOME/.kube/config
#
# - name: Update deployment
# run: |
# kubectl restart rollout deployment donat-user-deployment
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install kubectl
uses: azure/setup-kubectl@v1
with:
version: 'v1.21.0'
- name: Configure kube-config
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config
export KUBECONFIG=$HOME/.kube/config
- name: Update deployment
run: |
kubectl restart rollout deployment donat-user-deployment

View File

@ -4,29 +4,21 @@ import (
"context"
"donat-widget/internal/app/http"
"donat-widget/internal/config"
DonatRepo "donat-widget/internal/repository/donat"
DonatService "donat-widget/internal/service/donat"
)
import (
"donat-widget/infrastructure/pg"
"donat-widget/infrastructure/weed"
PaymentClient "donat-widget/pkg/api/payment"
)
import (
WidgetRepo "donat-widget/internal/repository/widget"
DonatRepo "donat-widget/internal/repository/widget/donat"
MediaRepo "donat-widget/internal/repository/widget/media"
WidgetService "donat-widget/internal/service/widget"
DonatService "donat-widget/internal/service/widget/donat"
MediaService "donat-widget/internal/service/widget/media"
)
// @title Widget service
// @version 1.0
// @description Описание.
// @host localhost:8002
// @BasePath /api/widget
func main() {
cfg := config.Init()
@ -34,20 +26,20 @@ func main() {
db := pg.NewPgPool(context.Background(), cfg.Db.Username, cfg.Db.Password, cfg.Db.Host, cfg.Db.Port, cfg.Db.DBName)
storage := weed.NewWeed(cfg.Storage.Filer, cfg.Storage.Master)
// CLIENTS
paymentClient := PaymentClient.New(cfg.PaymentService.Host, cfg.PaymentService.Port)
// REPOSITORIES
widgetRepo := WidgetRepo.New(db)
mediaRepo := MediaRepo.New(db, storage)
widgetRepo := WidgetRepo.New(db, storage)
donatRepo := DonatRepo.New(db)
// SERVICES
widgetService := WidgetService.New(widgetRepo, donatRepo)
mediaService := MediaService.New(mediaRepo)
donatService := DonatService.New(donatRepo)
donatService := DonatService.New(donatRepo, widgetRepo, paymentClient)
http.NewApp(
db,
widgetService,
mediaService,
donatService,
)
}

3
go.mod
View File

@ -5,8 +5,10 @@ go 1.23.0
require (
github.com/georgysavva/scany/v2 v2.1.3
github.com/go-playground/validator/v10 v10.22.0
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.6.0
github.com/labstack/echo/v4 v4.12.0
github.com/linxGnu/goseaweedfs v0.1.6
github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/swag v1.16.3
gopkg.in/yaml.v2 v2.4.0
@ -29,7 +31,6 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/linxGnu/goseaweedfs v0.1.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect

2
go.sum
View File

@ -30,6 +30,8 @@ github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=

View File

@ -0,0 +1,95 @@
package donat
import (
"context"
"donat-widget/internal/model"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"log/slog"
"net/http"
"strconv"
)
func CreateDonat(donatService model.DonatService) echo.HandlerFunc {
type Body 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 Body
if err := request.Bind(&body); err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
if err := request.Validate(&body); err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
orderID := model.OrderID(uuid.New().String())
err := donatService.CreateDonat(
ctx,
body.StreamerID,
orderID,
body.Amount,
body.Text,
body.DonatUser,
)
if err != nil {
return request.JSON(500, "Set donat error")
}
return request.String(200, "Set donat success")
}
}
func MarkDonatPaid(donatService model.DonatService) echo.HandlerFunc {
type Body struct {
OrderID model.OrderID `json:"orderID"`
}
return func(request echo.Context) error {
ctx := context.Background()
var body Body
if err := request.Bind(&body); err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
if err := request.Validate(&body); err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
err := donatService.MarkDonatPaid(ctx, body.OrderID)
if err != nil {
slog.Error("donatService.MarkDonatPaid error: " + err.Error())
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
return request.String(200, "Donat paid success")
}
}
func MarkDonatView(donatService model.DonatService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
donatID, err := strconv.Atoi(request.Param("donatID"))
if err != nil {
return echo.NewHTTPError(400, "Path parameter 'donatID' is invalid")
}
err = donatService.MarkDonatView(
ctx,
model.DonatID(donatID),
)
if err != nil {
return echo.NewHTTPError(500, "Delete donat error")
}
slog.Info("Delete donat success")
return request.String(200, "donat view success")
}
}

View File

@ -0,0 +1 @@
package target

View File

@ -1,76 +0,0 @@
package donat
import (
"context"
"donat-widget/internal/model"
"github.com/labstack/echo/v4"
"log/slog"
"strconv"
)
// SetDonat
//
// @Description Set donat
// @Tags Donat
// @Accept json
// @Produce json
// @Param RegisterData body SetDonatRequest true "Set donat"
// @Router /api/widget/donat/set [post]
func SetDonat(donatService model.DonatService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
var donatData SetDonatRequest
if err := request.Bind(&donatData); err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
err := request.Validate(&donatData)
if err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
err = donatService.SetDonat(
ctx,
donatData.WidgetID,
donatData.Text,
donatData.Amount,
donatData.DonatUser,
)
if err != nil {
return request.JSON(500, "Set donat error")
}
slog.Info("donat set success")
return request.String(200, "Set donat success")
}
}
// DeleteDonat
//
// @Description Delete donat
// @Tags Donat
// @Accept json
// @Produce json
// @Router /api/widget/donat/delete/{donatID} [post]
func DeleteDonat(donatService model.DonatService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
donatID, err := strconv.Atoi(request.Param("donatID"))
if err != nil {
return echo.NewHTTPError(400, "Path parameter 'donatID' is invalid")
}
err = donatService.DeleteDonat(
ctx,
model.DonatID(donatID),
)
if err != nil {
return echo.NewHTTPError(500, "Delete donat error")
}
slog.Info("Delete donat success")
return request.String(200, "Delete donat success")
}
}

View File

@ -1,10 +0,0 @@
package donat
import "donat-widget/internal/model"
type SetDonatRequest struct {
WidgetID model.WidgetID `json:"widgetID" validate:"required"`
Text string `json:"text" validate:"required"`
Amount model.DonatAmount `json:"amount" validate:"required"`
DonatUser string `json:"donatUser" validate:"required"`
}

View File

@ -1,165 +0,0 @@
package media
import (
"context"
"donat-widget/internal/model"
"github.com/labstack/echo/v4"
"log/slog"
"strconv"
)
// SetMediaFile
//
// @Description Upload media
// @Tags Media
// @Accept mpfd
// @Produce json
// @Param file formData file true "File to upload"
// @Param widgetId path int true "Widget ID"
// @Router /api/widget/media/{mediaType}/upload [post]
func SetMediaFile(service model.MediaService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
widgetID, err := strconv.Atoi(request.FormValue("widgetID"))
if err != nil {
return echo.NewHTTPError(404, "Path parameter 'widgetID' is invalid")
}
mediaType := request.Param("mediaType")
file, err := request.FormFile("file")
if err != nil {
return echo.NewHTTPError(404, "Form parameter 'file' is invalid")
}
src, err := file.Open()
if err != nil {
return echo.NewHTTPError(500, "File is invalid")
}
err = service.SetMediaFile(
ctx,
model.MediaType(mediaType),
model.WidgetID(widgetID),
&src,
file.Filename,
file.Size,
"",
)
if err != nil {
return echo.NewHTTPError(500, "File upload is failed")
}
slog.Info("set " + mediaType + " file successfully")
return request.String(200, "File successfully uploaded")
}
}
// GetMediaFile
//
// @Description Get media
// @Produce application/octet-stream
// @Success 200 {array} byte
// @Param widgetId path int true "Widget ID"
// @Param mediaType path string true "'background' or 'image' or 'audio'"
// @Router /api/widget/media/{mediaType}/get/{widgetID} [post]
func GetMediaFile(service model.MediaService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
mediaType := request.Param("mediaType")
if mediaType != "background" && mediaType != "image" && mediaType != "audio" {
return echo.NewHTTPError(400, "Path parameter 'mediaType' is invalid")
}
widgetID, err := strconv.Atoi(request.Param("widgetID"))
if err != nil {
return echo.NewHTTPError(400, "Path parameter 'widgetID' is invalid")
}
file, err := service.GetMediaFile(
ctx,
model.WidgetID(widgetID),
model.MediaType(mediaType),
)
if err != nil {
return echo.NewHTTPError(500, "Get File is failed")
}
slog.Info("get " + mediaType + " file successfully")
return request.Blob(200, "application/octet-stream", file)
}
}
func UpdateMediaFile(service model.MediaService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
widgetID, err := strconv.Atoi(request.FormValue("widgetID"))
if err != nil {
return echo.NewHTTPError(404, "Path parameter 'widgetID' is invalid")
}
mediaType := request.Param("mediaType")
file, err := request.FormFile("file")
if err != nil {
return echo.NewHTTPError(404, "Form parameter 'file' is invalid")
}
src, err := file.Open()
if err != nil {
return echo.NewHTTPError(500, "File is invalid")
}
err = service.UpdateMediaFile(
ctx,
model.WidgetID(widgetID),
model.MediaType(mediaType),
&src,
file.Filename,
file.Size,
"",
)
if err != nil {
return echo.NewHTTPError(500, "File update is failed")
}
slog.Info("update media file successfully")
return request.String(200, "Media successfully uploaded")
}
}
// SetMediaUrl
//
// @Description Set media URL
// @Param mediaType path string true "'background' or 'image' or 'audio'"
// @Router /api/widget/media/{mediaType}/set [post]
func SetMediaUrl(service model.MediaService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
var backgroundData SetRequest
if err := request.Bind(&backgroundData); err != nil {
return echo.NewHTTPError(400, err.Error())
}
mediaType := request.Param("mediaType")
err := request.Validate(&backgroundData)
if err != nil {
return echo.NewHTTPError(400, err.Error())
}
err = service.SetMediaUrl(
ctx,
model.MediaType(mediaType),
backgroundData.WidgetID,
backgroundData.MediaUrl,
)
if err != nil {
return echo.NewHTTPError(500, "Set MediaUrl is failed")
}
slog.Info("Set media url successfully")
return request.String(200, "Media URL successfully set")
}
}

View File

@ -1,8 +0,0 @@
package media
import "donat-widget/internal/model"
type SetRequest struct {
WidgetID model.WidgetID `json:"widgetID" validate:"required"`
MediaUrl model.MediaUrl `json:"mediaUrl" validate:"required"`
}

View File

@ -1,27 +0,0 @@
package widget
import "donat-widget/internal/model"
type CreateWidgetRequest struct {
StreamerID model.StreamerID `json:"streamerID" validate:"required"`
TemplateID model.TemplateID `json:"templateID" validate:"required"`
}
type CreateWidgetResponse struct {
WidgetID model.WidgetID `json:"widgetID"`
}
type GetWidgetInfoResponse struct {
AudioUrl model.MediaUrl `json:"audioUrl"`
ImageUrl model.MediaUrl `json:"imageUrl"`
Text string `json:"text"`
Amount model.DonatAmount `json:"amount"`
DonatUser string `json:"donatUser"`
Display model.Display `json:"display"`
Duration model.Duration `json:"duration"`
DonatID model.DonatID `json:"donatID"`
}
type UpdateDurationRequest struct {
WidgetID model.WidgetID `json:"widgetID" validate:"required"`
Duration model.Duration `json:"duration" validate:"required"`
}

View File

@ -8,53 +8,42 @@ import (
"strconv"
)
// CreateWidget
//
// @Description Create widget
// @Tags Widget
// @Accept json
// @Produce json
// @Param RegisterData body CreateWidgetRequest true "Create widget"
// @Success 200 {object} CreateWidgetResponse
// @Router /api/widget/create [post]
func CreateWidget(widgetService model.WidgetService) echo.HandlerFunc {
type Body struct {
StreamerID model.StreamerID `json:"streamerID" validate:"required"`
TemplateID model.TemplateID `json:"templateID" validate:"required"`
}
type Response struct {
WidgetID model.WidgetID `json:"widgetID"`
}
return func(request echo.Context) error {
ctx := context.Background()
var widgetData CreateWidgetRequest
if err := request.Bind(&widgetData); err != nil {
var body Body
if err := request.Bind(&body); err != nil {
return echo.NewHTTPError(400, err.Error())
}
err := request.Validate(&widgetData)
if err != nil {
if err := request.Validate(&body); err != nil {
return echo.NewHTTPError(400, err.Error())
}
widgetID, err := widgetService.CreateWidget(
ctx,
widgetData.StreamerID,
widgetData.TemplateID,
body.StreamerID,
body.TemplateID,
)
if err != nil {
return request.JSON(422, "Create widget error")
}
response := CreateWidgetResponse{
response := Response{
WidgetID: widgetID,
}
slog.Info("Widget created")
return request.JSON(200, response)
}
}
// GetWidgetHTML @Description Get widget
//
// @Tags Widget
// @Accept json
// @Produce json
// @Success 200
// @Router /api/widget/html/{widgetID} [get]
func GetWidgetHTML(widgetService model.WidgetService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
@ -76,15 +65,17 @@ func GetWidgetHTML(widgetService model.WidgetService) echo.HandlerFunc {
}
}
// GetWidgetInfo
//
// @Description Widget Info
// @Tags Widget
// @Accept json
// @Produce json
// @Param widgetID path int true "Widget ID"
// @Router /api/widget/info/{widgetID} [get]
func GetWidgetInfo(widgetService model.WidgetService) echo.HandlerFunc {
type Response struct {
AudioUrl model.MediaUrl `json:"audioUrl"`
ImageUrl model.MediaUrl `json:"imageUrl"`
Text string `json:"text"`
Amount model.DonatAmount `json:"amount"`
DonatUser string `json:"donatUser"`
Display model.Display `json:"display"`
Duration model.Duration `json:"duration"`
DonatID model.DonatID `json:"donatID"`
}
return func(request echo.Context) error {
ctx := context.Background()
@ -99,14 +90,13 @@ func GetWidgetInfo(widgetService model.WidgetService) echo.HandlerFunc {
}
if !donatAndWidget.Display {
response := GetWidgetInfoResponse{
response := Response{
Display: donatAndWidget.Display,
}
slog.Info("Get widget info successfully")
return request.JSON(200, response)
}
response := GetWidgetInfoResponse{
response := Response{
AudioUrl: donatAndWidget.Widget.AudioUrl,
ImageUrl: donatAndWidget.Widget.ImageUrl,
Text: donatAndWidget.Donat.Text,
@ -116,44 +106,171 @@ func GetWidgetInfo(widgetService model.WidgetService) echo.HandlerFunc {
Amount: donatAndWidget.Donat.Amount,
DonatID: donatAndWidget.Donat.ID,
}
slog.Info("Get widget info successfully")
return request.JSON(200, response)
}
}
// UpdateDuration
//
// @Description UpdateDuration
// @Tags Widget
// @Accept json
// @Produce json
// @Param UpdateData body UpdateDurationRequest true "UpdateDuration"
// @Success 200
// @Router /api/widget/duration/update [post]
func UpdateDuration(widgetService model.WidgetService) echo.HandlerFunc {
type Body struct {
WidgetID model.WidgetID `json:"widgetID" validate:"required"`
Duration model.Duration `json:"duration" validate:"required"`
}
return func(request echo.Context) error {
ctx := context.Background()
var widgetData UpdateDurationRequest
if err := request.Bind(&widgetData); err != nil {
var body Body
if err := request.Bind(&body); err != nil {
return echo.NewHTTPError(400, err.Error())
}
if err := request.Validate(&body); err != nil {
return echo.NewHTTPError(400, err.Error())
}
err := request.Validate(&widgetData)
if err != nil {
return echo.NewHTTPError(400, err.Error())
}
err = widgetService.UpdateDuration(
err := widgetService.UpdateDuration(
ctx,
widgetData.WidgetID,
widgetData.Duration,
body.WidgetID,
body.Duration,
)
if err != nil {
return request.JSON(422, "Update duration error")
}
slog.Info("Duration updated")
return request.JSON(200, "Update duration successfully")
}
}
func SetMediaFile(service model.MediaService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
widgetID, err := strconv.Atoi(request.FormValue("widgetID"))
if err != nil {
return echo.NewHTTPError(404, "Path parameter 'widgetID' is invalid")
}
mediaType := request.Param("mediaType")
file, err := request.FormFile("file")
if err != nil {
return echo.NewHTTPError(404, "Form parameter 'file' is invalid")
}
src, err := file.Open()
if err != nil {
return echo.NewHTTPError(500, "File is invalid")
}
err = service.SetMediaFile(
ctx,
model.MediaType(mediaType),
model.WidgetID(widgetID),
&src,
file.Filename,
file.Size,
"",
)
if err != nil {
return echo.NewHTTPError(500, "File upload is failed")
}
slog.Info("set " + mediaType + " file successfully")
return request.String(200, "File successfully uploaded")
}
}
func GetMediaFile(service model.MediaService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
mediaType := request.Param("mediaType")
if mediaType != "background" && mediaType != "image" && mediaType != "audio" {
return echo.NewHTTPError(400, "Path parameter 'mediaType' is invalid")
}
widgetID, err := strconv.Atoi(request.Param("widgetID"))
if err != nil {
return echo.NewHTTPError(400, "Path parameter 'widgetID' is invalid")
}
file, err := service.GetMediaFile(
ctx,
model.WidgetID(widgetID),
model.MediaType(mediaType),
)
if err != nil {
return echo.NewHTTPError(500, "Get File is failed")
}
slog.Info("get " + mediaType + " file successfully")
return request.Blob(200, "application/octet-stream", file)
}
}
func UpdateMediaFile(service model.MediaService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
widgetID, err := strconv.Atoi(request.FormValue("widgetID"))
if err != nil {
return echo.NewHTTPError(404, "Path parameter 'widgetID' is invalid")
}
mediaType := request.Param("mediaType")
file, err := request.FormFile("file")
if err != nil {
return echo.NewHTTPError(404, "Form parameter 'file' is invalid")
}
src, err := file.Open()
if err != nil {
return echo.NewHTTPError(500, "File is invalid")
}
err = service.UpdateMediaFile(
ctx,
model.WidgetID(widgetID),
model.MediaType(mediaType),
&src,
file.Filename,
file.Size,
"",
)
if err != nil {
return echo.NewHTTPError(500, "File update is failed")
}
slog.Info("update media file successfully")
return request.String(200, "Media successfully uploaded")
}
}
func SetMediaUrl(service model.MediaService) echo.HandlerFunc {
type Body struct {
WidgetID model.WidgetID `json:"widgetID" validate:"required"`
MediaUrl model.MediaUrl `json:"mediaUrl" validate:"required"`
}
return func(request echo.Context) error {
ctx := context.Background()
var body Body
if err := request.Bind(&body); err != nil {
return echo.NewHTTPError(400, err.Error())
}
if err := request.Validate(&body); err != nil {
return echo.NewHTTPError(400, err.Error())
}
mediaType := request.Param("mediaType")
err := service.SetMediaUrl(
ctx,
model.MediaType(mediaType),
body.WidgetID,
body.MediaUrl,
)
if err != nil {
return echo.NewHTTPError(500, "Set MediaUrl is failed")
}
return request.String(200, "Media URL successfully set")
}
}

View File

@ -2,6 +2,7 @@ package http
import (
"context"
. "donat-widget/internal/api/http/handlers/donat"
"donat-widget/internal/model/sql"
"github.com/labstack/echo/v4"
"github.com/swaggo/echo-swagger"
@ -15,8 +16,6 @@ import (
import (
. "donat-widget/internal/api/http/handlers/widget"
. "donat-widget/internal/api/http/handlers/widget/donat"
. "donat-widget/internal/api/http/handlers/widget/media"
)
var PREFIX = "/api/widget"
@ -24,7 +23,6 @@ var PREFIX = "/api/widget"
func NewApp(
db model.Db,
widgetService model.WidgetService,
mediaService model.MediaService,
donatService model.DonatService,
) {
server := echo.New()
@ -35,7 +33,6 @@ func NewApp(
server.GET(PREFIX+"/table/drop", DropTale(db))
IncludeWidgetHandlers(server, widgetService)
IncludeMediaHandlers(server, mediaService)
IncludeDonatHandlers(server, donatService)
server.Logger.Fatal(server.Start(":8002"))
@ -45,23 +42,18 @@ func IncludeDonatHandlers(
server *echo.Echo,
donatService model.DonatService,
) {
server.POST(PREFIX+"/donat/set", SetDonat(donatService))
server.DELETE(PREFIX+"/donat/delete/:donatID", DeleteDonat(donatService))
}
func IncludeMediaHandlers(
server *echo.Echo,
mediaService model.MediaService,
) {
server.POST(PREFIX+"/media/:mediaType/upload", SetMediaFile(mediaService))
server.POST(PREFIX+"/media/:mediaType/set", SetMediaUrl(mediaService))
server.GET(PREFIX+"/media/:mediaType/get/:widgetID", GetMediaFile(mediaService))
server.POST(PREFIX+"/donat/create", CreateDonat(donatService))
server.POST(PREFIX+"/donat/view/:donatID", MarkDonatView(donatService))
server.POST(PREFIX+"/donat/paid/:donatID", MarkDonatPaid(donatService))
}
func IncludeWidgetHandlers(
server *echo.Echo,
widgetService model.WidgetService,
) {
server.POST(PREFIX+"/media/:mediaType/upload", SetMediaFile(widgetService))
server.POST(PREFIX+"/media/:mediaType/set", SetMediaUrl(widgetService))
server.GET(PREFIX+"/media/:mediaType/get/:widgetID", GetMediaFile(widgetService))
server.POST(PREFIX+"/create", CreateWidget(widgetService))
server.PATCH(PREFIX+"/duration/update", UpdateDuration(widgetService))
server.GET(PREFIX+"/html/:widgetID", GetWidgetHTML(widgetService))

View File

@ -6,8 +6,9 @@ import (
)
type Config struct {
Db Database `yaml:"db"`
Storage Storage `yaml:"storage"`
Db Database `yaml:"db"`
Storage Storage `yaml:"storage"`
PaymentService PaymentService `yaml:"paymentService"`
}
type Database struct {
@ -23,6 +24,11 @@ type Storage struct {
Master string `yaml:"master"`
}
type PaymentService struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
}
func Init() *Config {
data, err := os.ReadFile("internal/config/config.yaml")
if err != nil {

View File

@ -10,4 +10,8 @@ server:
storage:
filer: "http://92.63.193.151:8111"
master: "http://92.63.193.151:9333"
master: "http://92.63.193.151:9333"
paymentService:
host: "payment-service"
port: "8003"

View File

@ -0,0 +1,12 @@
package api
type CreatePaymentResponse struct {
Amount int `json:"Amount"`
ErrorCode string `json:"ErrorCode"`
OrderId string `json:"OrderId"`
PaymentId string `json:"PaymentId"`
PaymentURL string `json:"PaymentURL"`
Status string `json:"Status"`
Success bool `json:"Success"`
TerminalKey string `json:"TerminalKey"`
}

View File

@ -2,6 +2,7 @@ package model
import (
"context"
"donat-widget/internal/model/api"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
@ -13,6 +14,10 @@ type WidgetService interface {
UpdateDuration(ctx context.Context, widgetID WidgetID, duration Duration) error
GetWidgetInfo(ctx context.Context, widgetID WidgetID) (*DonatAndWidget, error)
GetWidgetHTML(ctx context.Context, widgetID WidgetID) (WidgetHTML, error)
SetMediaFile(ctx context.Context, mediaType MediaType, widgetID WidgetID, file UploadFile, filename string, size int64, collection string) error
SetMediaUrl(ctx context.Context, mediaType MediaType, widgetID WidgetID, mediaURL MediaUrl) error
UpdateMediaFile(ctx context.Context, widgetID WidgetID, mediaType MediaType, file UploadFile, filename string, size int64, collection string) error
GetMediaFile(ctx context.Context, widgetID WidgetID, mediaType MediaType) (DownloadFile, error)
}
type WidgetRepo interface {
@ -20,16 +25,7 @@ type WidgetRepo interface {
DeleteWidget(ctx context.Context, widgetID WidgetID) error
UpdateDuration(ctx context.Context, widgetID WidgetID, duration Duration) error
GetWidget(ctx context.Context, widgetID WidgetID) (*Widget, error)
}
type MediaService interface {
SetMediaFile(ctx context.Context, mediaType MediaType, widgetID WidgetID, file UploadFile, filename string, size int64, collection string) error
SetMediaUrl(ctx context.Context, mediaType MediaType, widgetID WidgetID, mediaURL MediaUrl) error
UpdateMediaFile(ctx context.Context, widgetID WidgetID, mediaType MediaType, file UploadFile, filename string, size int64, collection string) error
GetMediaFile(ctx context.Context, widgetID WidgetID, mediaType MediaType) (DownloadFile, error)
}
type MediaRepo interface {
GetAllWidget(ctx context.Context, streamerID StreamerID) ([]*Widget, error)
SetMediaFile(file UploadFile, filename string, size int64, collection string) (FileID, error)
GetMediaFile(fileID FileID) (DownloadFile, error)
GetMediaUrl(ctx context.Context, widgetID WidgetID, mediaType MediaType) (MediaUrl, error)
@ -38,14 +34,16 @@ type MediaRepo interface {
}
type DonatService interface {
SetDonat(ctx context.Context, widgetID WidgetID, text string, amount DonatAmount, donatUser string) error
DeleteDonat(ctx context.Context, DonatID DonatID) error
CreateDonat(ctx context.Context, streamerID StreamerID, orderID OrderID, amount DonatAmount, text string, donatUser string) error
MarkDonatPaid(ctx context.Context, orderID OrderID) error
MarkDonatView(ctx context.Context, DonatID DonatID) error
}
type DonatRepo interface {
SetDonat(ctx context.Context, widgetID WidgetID, text string, amount DonatAmount, donatUser string) error
CreateDonat(ctx context.Context, widgetID WidgetID, orderID OrderID, amount DonatAmount, text string, donatUser string) error
GetDonat(ctx context.Context, widgetID WidgetID) ([]*Donat, error)
DeleteDonat(ctx context.Context, donatID DonatID) error
MarkDonatPaid(ctx context.Context, orderID OrderID) error
MarkDonatView(ctx context.Context, DonatID DonatID) error
}
type Error interface {
@ -58,6 +56,9 @@ type Storage interface {
Update(file UploadFile, fileID FileID, filename string, size int64, collection string) error
}
type PaymentClient interface {
CreatePayment(streamerID StreamerID, amount DonatAmount, orderID OrderID) (api.CreatePaymentResponse, error)
}
type Db interface {
Exec(ctx context.Context, query string, args ...interface{}) (pgconn.CommandTag, error)
Query(ctx context.Context, query string, args ...interface{}) (pgx.Rows, error)

View File

@ -7,15 +7,51 @@ import (
)
type Widget struct {
ID WidgetID
StreamerID StreamerID
TemplateID TemplateID
BackgroundUrl MediaUrl
ImageUrl MediaUrl
AudioUrl MediaUrl
Duration Duration
CreatedAt time.Time
UpdatedAt time.Time
ID WidgetID `db:"id"`
StreamerID StreamerID `db:"streamer_id"`
TemplateID TemplateID `db:"template_id"`
BackgroundUrl MediaUrl `db:"background_url"`
ImageUrl MediaUrl `db:"image_url"`
AudioUrl MediaUrl `db:"audio_url"`
MinAmount DonatAmount `db:"min_amount"`
Duration Duration `db:"duration"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type Donat struct {
ID DonatID `db:"id"`
WidgetID WidgetID `db:"widget_id"`
Text string `db:"text"`
DonatUser string `db:"donat_user"`
Amount DonatAmount `db:"amount"`
Paid bool `db:"paid"`
View bool `db:"view"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type Target struct {
ID TargetID `db:"id"`
Text string `db:"text"`
Collected DonatAmount `db:"collected"`
Amount DonatAmount `db:"amount"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type DonatAndWidget struct {
Widget *Widget
Donat *Donat
Display Display
}
func (widget *Widget) GetMediaUrl(mediaType MediaType) MediaUrl {
@ -45,19 +81,3 @@ func (widget *Widget) NormalizeUrl() {
widget.AudioUrl = MediaUrl(selfDomain + "/audio/get/" + strWidgetID)
}
}
type Donat struct {
ID DonatID
WidgetID WidgetID
Text string
DonatUser string
Amount DonatAmount
CreatedAt time.Time
UpdatedAt time.Time
}
type DonatAndWidget struct {
Widget *Widget
Donat *Donat
Display Display
}

View File

@ -5,10 +5,14 @@ CREATE TABLE IF NOT EXISTS widgets (
id SERIAL PRIMARY KEY,
streamer_id INTEGER NOT NULL,
template_id INTEGER NOT NULL,
background_url TEXT DEFAULT '',
image_url TEXT DEFAULT '',
audio_url TEXT DEFAULT '',
duration INTEGER DEFAULT 30,
duration INTEGER NOT NULL,
min_amount INTEGER NOT NUll,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
@ -16,14 +20,30 @@ CREATE TABLE IF NOT EXISTS widgets (
CREATE TABLE IF NOT EXISTS donats (
id SERIAL PRIMARY KEY,
widget_id INTEGER NOT NULL,
text TEXT DEFAULT '',
amount TEXT DEFAULT '',
donat_user TEXT DEFAULT '',
order_id TEXT NOT NULL,
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
);
CREATE OR REPLACE FUNCTION update_updated_at()
CREATE TABLE IF NOT EXISTS targets (
id SERIAL PRIMARY KEY,
text TEXT NOT NULL,
amount INTEGER NOT NULL,
collected INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
@ -40,9 +60,15 @@ CREATE TRIGGER update_updated_at_trigger
BEFORE UPDATE ON donats
FOR EACH ROW
EXECUTE PROCEDURE update_updated_at();
CREATE TRIGGER update_updated_at_trigger
BEFORE UPDATE ON targets
FOR EACH ROW
EXECUTE PROCEDURE update_updated_at();
`
var DropTableQuery = `
DROP TABLE IF EXISTS widgets;
DROP TABLE IF EXISTS donats;
DROP TABLE IF EXISTS targets;
`

View File

@ -5,27 +5,21 @@ import (
"fmt"
)
var CreateWidgetQuery = `
var CreateWidget = `
INSERT INTO widgets (streamer_id, template_id)
VALUES (@streamer_id, @template_id);
`
var SetDonatQuery = `
INSERT INTO donats (widget_id, text, amount, donat_user)
VALUES (@widget_id, @text, @amount, @donat_user);
`
var DeleteDonatQuery = `
DELETE FROM donats WHERE id = (@id);
`
var UpdateDurationQuery = `
var UpdateDuration = `
UPDATE widgets
SET duration = (@duration)
WHERE id = (@id)
`
var GetWidget = `
SELECT * FROM widgets
WHERE id = (@id);
`
func UpdateMediaUrlQuery(mediaType model.MediaType) string {
func UpdateMediaUrl(mediaType model.MediaType) string {
query := fmt.Sprintf(`
UPDATE widgets
SET %s = (@%s)
@ -34,13 +28,9 @@ func UpdateMediaUrlQuery(mediaType model.MediaType) string {
return query
}
var GetDonatQuery = `
SELECT * FROM donats WHERE widget_id = (@widget_id);
`
var GetWidgetQuery = `
var GetAllWidget = `
SELECT * FROM widgets
WHERE id = (@id);
WHERE streamer_id = (@streamer_id);
`
func GetMediaUrl(mediaType model.MediaType) string {
@ -51,3 +41,22 @@ func GetMediaUrl(mediaType model.MediaType) string {
`, mediaType)
return query
}
var CreateDonat = `
INSERT INTO donats (widget_id, text, amount, donat_user, order_id)
VALUES (@widget_id, @text, @amount, @donat_user, @order_id);
`
var MarkDonatView = `
UPDATE donats
SET view = (@view)
WHERE id = (@id);
`
var MarkDonatPaid = `
UPDATE donats
SET paid = (@paid)
WHERE order_id = (@order_id);
`
var GetDonat = `
SELECT * FROM donats
WHERE widget_id = (@widget_id) AND paid = (@paid) AND view = (@view);
`

View File

@ -23,5 +23,9 @@ type UploadFile *multipart.File
type DownloadFile []byte
type FileID string
type DonatAmount string
type DonatAmount int
type DonatID int
type OrderID string
type TargetID int
type Collected int

View File

@ -19,20 +19,22 @@ type RepoDonat struct {
db model.Db
}
func (repoDonat *RepoDonat) SetDonat(
func (repoDonat *RepoDonat) CreateDonat(
ctx context.Context,
widgetID model.WidgetID,
text string,
orderID model.OrderID,
amount model.DonatAmount,
text string,
donatUser string,
) error {
args := model.QueryArgs{
"widget_id": widgetID,
"order_id": orderID,
"text": text,
"amount": amount,
"donat_user": donatUser,
}
_, err := repoDonat.db.Query(ctx, sql.SetDonatQuery, args)
_, err := repoDonat.db.Query(ctx, sql.CreateDonat, args)
if err != nil {
slog.Error("repoDonat.db.Query: " + err.Error())
return err
@ -47,8 +49,10 @@ func (repoDonat *RepoDonat) GetDonat(
) ([]*model.Donat, error) {
args := pgx.NamedArgs{
"widget_id": widgetID,
"paid": true,
"view": false,
}
rows, err := repoDonat.db.Query(ctx, sql.GetDonatQuery, args)
rows, err := repoDonat.db.Query(ctx, sql.GetDonat, args)
if err != nil {
slog.Error("repoDonat.db.Query: " + err.Error())
return nil, err
@ -64,14 +68,32 @@ func (repoDonat *RepoDonat) GetDonat(
return donats, nil
}
func (repoDonat *RepoDonat) DeleteDonat(
func (repoDonat *RepoDonat) MarkDonatView(
ctx context.Context,
donatID model.DonatID,
) error {
args := pgx.NamedArgs{
"id": donatID,
"id": donatID,
"view": true,
}
_, err := repoDonat.db.Query(ctx, sql.DeleteDonatQuery, args)
_, err := repoDonat.db.Query(ctx, sql.MarkDonatView, args)
if err != nil {
slog.Error("repoDonat.db.Query: " + err.Error())
return err
}
return nil
}
func (repoDonat *RepoDonat) MarkDonatPaid(
ctx context.Context,
orderID model.OrderID,
) error {
args := pgx.NamedArgs{
"order_id": orderID,
"paid": true,
}
_, err := repoDonat.db.Query(ctx, sql.MarkDonatPaid, args)
if err != nil {
slog.Error("repoDonat.db.Query: " + err.Error())
return err

View File

@ -0,0 +1 @@
package target

View File

@ -1,135 +0,0 @@
package media
import (
"context"
"donat-widget/internal/model"
"donat-widget/internal/model/sql"
"errors"
"github.com/georgysavva/scany/v2/pgxscan"
"log/slog"
)
func New(
db model.Db,
storage model.Storage,
) *RepoMedia {
return &RepoMedia{
db: db,
storage: storage,
}
}
type RepoMedia struct {
db model.Db
storage model.Storage
}
func (repoMedia *RepoMedia) SetMediaFile(
file model.UploadFile,
filename string,
size int64,
collection string,
) (model.FileID, error) {
fileData, err := repoMedia.storage.Upload(file, filename, size, collection)
if err != nil {
slog.Error("repoMedia.storage.Upload: " + err.Error())
return "", err
}
return model.FileID(fileData.FileID), nil
}
func (repoMedia *RepoMedia) GetMediaFile(
fileID model.FileID,
) (model.DownloadFile, error) {
file, err := repoMedia.storage.Download(fileID)
if err != nil {
slog.Error("repoMedia.storage.Download: " + err.Error())
return nil, err
}
return file, err
}
func (repoMedia *RepoMedia) UpdateMediaFile(
ctx context.Context,
widgetID model.WidgetID,
file model.UploadFile,
fileID model.FileID,
filename string,
size int64,
collection string,
mediaType model.MediaType,
) error {
err := repoMedia.storage.Update(file, fileID, filename, size, collection)
if err != nil {
slog.Error("repoMedia.storage.Update: " + err.Error())
return err
}
mediaType = mediaType + "_url"
args := model.QueryArgs{
string(mediaType): model.MediaUrl(fileID),
"id": widgetID,
}
_, err = repoMedia.db.Query(ctx, sql.UpdateMediaUrlQuery(mediaType), args)
if err != nil {
slog.Error("repoMedia.db.Query: " + err.Error())
return err
}
return nil
}
func (repoMedia *RepoMedia) SetMediaUrl(
ctx context.Context,
widgetID model.WidgetID,
mediaUrl model.MediaUrl,
mediaType model.MediaType,
) error {
mediaType = mediaType + "_url"
args := model.QueryArgs{
string(mediaType): mediaUrl,
"id": widgetID,
}
_, err := repoMedia.db.Query(ctx, sql.UpdateMediaUrlQuery(mediaType), args)
if err != nil {
slog.Error("repoMedia.db.Query: " + err.Error())
return err
}
return nil
}
func (repoMedia *RepoMedia) GetMediaUrl(
ctx context.Context,
widgetID model.WidgetID,
mediaType model.MediaType,
) (model.MediaUrl, error) {
mediaType = mediaType + "_url"
args := model.QueryArgs{
"id": widgetID,
}
rows, err := repoMedia.db.Query(ctx, sql.GetMediaUrl(mediaType), args)
if err != nil {
slog.Error("repoMedia.db.Query: " + err.Error())
return "", err
}
var widgets []*model.Widget
err = pgxscan.ScanAll(&widgets, rows)
if err != nil {
slog.Error("repoMedia.pgxscan.ScanAll: " + err.Error())
return "", err
}
if len(widgets) == 0 {
slog.Error("Widget does not exist")
return "", errors.New("widget does not exist")
}
widget := widgets[0]
mediaUrl := widget.GetMediaUrl(mediaType)
return mediaUrl, nil
}

View File

@ -9,14 +9,19 @@ import (
"log/slog"
)
func New(db model.Db) *RepoWidget {
func New(
db model.Db,
storage model.Storage,
) *RepoWidget {
return &RepoWidget{
db: db,
db: db,
storage: storage,
}
}
type RepoWidget struct {
db model.Db
db model.Db
storage model.Storage
}
func (widgetRepo *RepoWidget) CreateWidget(
@ -28,7 +33,7 @@ func (widgetRepo *RepoWidget) CreateWidget(
"streamer_id": streamerID,
"template_id": templateID,
}
_, err := widgetRepo.db.Query(ctx, sql.CreateWidgetQuery, args)
_, err := widgetRepo.db.Query(ctx, sql.CreateWidget, args)
if err != nil {
slog.Error("widgetRepo.db.Query: " + err.Error())
return 0, err
@ -45,7 +50,7 @@ func (widgetRepo *RepoWidget) GetWidget(
args := model.QueryArgs{
"id": widgetID,
}
rows, err := widgetRepo.db.Query(ctx, sql.GetWidgetQuery, args)
rows, err := widgetRepo.db.Query(ctx, sql.GetWidget, args)
if err != nil {
slog.Error("widgetRepo.db.Query: " + err.Error())
return nil, err
@ -69,6 +74,29 @@ func (widgetRepo *RepoWidget) GetWidget(
return widget, nil
}
func (widgetRepo *RepoWidget) GetAllWidget(
ctx context.Context,
streamerID model.StreamerID,
) ([]*model.Widget, error) {
args := model.QueryArgs{
"streamerID": streamerID,
}
rows, err := widgetRepo.db.Query(ctx, sql.GetAllWidget, args)
if err != nil {
slog.Error("widgetRepo.db.Query: " + err.Error())
return nil, err
}
var widgets []*model.Widget
err = pgxscan.ScanAll(&widgets, rows)
if err != nil {
slog.Error(err.Error())
return nil, err
}
return widgets, nil
}
func (widgetRepo *RepoWidget) DeleteWidget(
ctx context.Context,
widgetID model.WidgetID,
@ -85,7 +113,7 @@ func (widgetRepo *RepoWidget) UpdateDuration(
"id": widgetID,
"duration": duration,
}
_, err := widgetRepo.db.Query(ctx, sql.UpdateDurationQuery, args)
_, err := widgetRepo.db.Query(ctx, sql.UpdateDuration, args)
if err != nil {
slog.Error("widgetRepo.db.Query: " + err.Error())
return err
@ -93,3 +121,113 @@ func (widgetRepo *RepoWidget) UpdateDuration(
return nil
}
func (widgetRepo *RepoWidget) SetMediaFile(
file model.UploadFile,
filename string,
size int64,
collection string,
) (model.FileID, error) {
fileData, err := widgetRepo.storage.Upload(file, filename, size, collection)
if err != nil {
slog.Error("repoMedia.storage.Upload: " + err.Error())
return "", err
}
return model.FileID(fileData.FileID), nil
}
func (widgetRepo *RepoWidget) GetMediaFile(
fileID model.FileID,
) (model.DownloadFile, error) {
file, err := widgetRepo.storage.Download(fileID)
if err != nil {
slog.Error("repoMedia.storage.Download: " + err.Error())
return nil, err
}
return file, err
}
func (widgetRepo *RepoWidget) UpdateMediaFile(
ctx context.Context,
widgetID model.WidgetID,
file model.UploadFile,
fileID model.FileID,
filename string,
size int64,
collection string,
mediaType model.MediaType,
) error {
err := widgetRepo.storage.Update(file, fileID, filename, size, collection)
if err != nil {
slog.Error("repoMedia.storage.Update: " + err.Error())
return err
}
mediaType = mediaType + "_url"
args := model.QueryArgs{
string(mediaType): model.MediaUrl(fileID),
"id": widgetID,
}
_, err = widgetRepo.db.Query(ctx, sql.UpdateMediaUrl(mediaType), args)
if err != nil {
slog.Error("repoMedia.db.Query: " + err.Error())
return err
}
return nil
}
func (widgetRepo *RepoWidget) SetMediaUrl(
ctx context.Context,
widgetID model.WidgetID,
mediaUrl model.MediaUrl,
mediaType model.MediaType,
) error {
mediaType = mediaType + "_url"
args := model.QueryArgs{
string(mediaType): mediaUrl,
"id": widgetID,
}
_, err := widgetRepo.db.Query(ctx, sql.UpdateMediaUrl(mediaType), args)
if err != nil {
slog.Error("repoMedia.db.Query: " + err.Error())
return err
}
return nil
}
func (widgetRepo *RepoWidget) GetMediaUrl(
ctx context.Context,
widgetID model.WidgetID,
mediaType model.MediaType,
) (model.MediaUrl, error) {
mediaType = mediaType + "_url"
args := model.QueryArgs{
"id": widgetID,
}
rows, err := widgetRepo.db.Query(ctx, sql.GetMediaUrl(mediaType), args)
if err != nil {
slog.Error("repoMedia.db.Query: " + err.Error())
return "", err
}
var widgets []*model.Widget
err = pgxscan.ScanAll(&widgets, rows)
if err != nil {
slog.Error("repoMedia.pgxscan.ScanAll: " + err.Error())
return "", err
}
if len(widgets) == 0 {
slog.Error("Widget does not exist")
return "", errors.New("widget does not exist")
}
widget := widgets[0]
mediaUrl := widget.GetMediaUrl(mediaType)
return mediaUrl, nil
}

View File

@ -0,0 +1,92 @@
package donat
import (
"context"
"donat-widget/internal/model"
"log/slog"
)
type ServiceDonat struct {
donatRepo model.DonatRepo
widgetRepo model.WidgetRepo
paymentClient model.PaymentClient
}
func New(
donatRepo model.DonatRepo,
widgetRepo model.WidgetRepo,
paymentClient model.PaymentClient,
) *ServiceDonat {
return &ServiceDonat{
donatRepo: donatRepo,
widgetRepo: widgetRepo,
paymentClient: paymentClient,
}
}
func (donatService *ServiceDonat) CreateDonat(
ctx context.Context,
streamerID model.StreamerID,
orderID model.OrderID,
amount model.DonatAmount,
text string,
donatUser string,
) error {
widgets, err := donatService.widgetRepo.GetAllWidget(ctx, streamerID)
if err != nil {
slog.Error("donatService.widgetRepo.GetAllWidget: ", err)
return err
}
var widgetID model.WidgetID
for _, widget := range widgets {
if widget.MinAmount <= amount {
widgetID = widget.ID
}
}
err = donatService.donatRepo.CreateDonat(
ctx,
widgetID,
orderID,
amount,
text,
donatUser,
)
if err != nil {
slog.Error("donatService.donatRepo.SetDonat: " + err.Error())
return err
}
return nil
}
func (donatService *ServiceDonat) MarkDonatPaid(
ctx context.Context,
orderID model.OrderID,
) error {
err := donatService.donatRepo.MarkDonatPaid(
ctx,
orderID,
)
if err != nil {
slog.Error("donatService.donatRepo.MarkDonatView: " + err.Error())
return err
}
return nil
}
func (donatService *ServiceDonat) MarkDonatView(
ctx context.Context,
donatID model.DonatID,
) error {
err := donatService.donatRepo.MarkDonatView(
ctx,
donatID,
)
if err != nil {
slog.Error("donatService.donatRepo.MarkDonatView: " + err.Error())
return err
}
return nil
}

View File

@ -0,0 +1 @@
package target

View File

@ -1,55 +0,0 @@
package donat
import (
"context"
"donat-widget/internal/model"
"log/slog"
)
type ServiceDonat struct {
donatRepo model.DonatRepo
}
func New(
donatRepo model.DonatRepo,
) *ServiceDonat {
return &ServiceDonat{
donatRepo: donatRepo,
}
}
func (donatService *ServiceDonat) SetDonat(
ctx context.Context,
widgetID model.WidgetID,
text string,
amount model.DonatAmount,
donatUser string,
) error {
err := donatService.donatRepo.SetDonat(
ctx,
widgetID,
text,
amount,
donatUser,
)
if err != nil {
slog.Error("donatService.donatRepo.SetDonat: " + err.Error())
return err
}
return nil
}
func (donatService *ServiceDonat) DeleteDonat(
ctx context.Context,
donatID model.DonatID,
) error {
err := donatService.donatRepo.DeleteDonat(
ctx,
donatID,
)
if err != nil {
slog.Error("donatService.donatRepo.DeleteDonat: " + err.Error())
return err
}
return nil
}

View File

@ -1,131 +0,0 @@
package media
import (
"context"
"donat-widget/internal/model"
"log/slog"
)
func New(mediaRepo model.MediaRepo) *ServiceMedia {
return &ServiceMedia{
mediaRepo: mediaRepo,
}
}
type ServiceMedia struct {
mediaRepo model.MediaRepo
}
func (mediaService *ServiceMedia) SetMediaFile(
ctx context.Context,
mediaType model.MediaType,
widgetID model.WidgetID,
file model.UploadFile,
filename string,
size int64,
collection string,
) error {
fileID, err := mediaService.mediaRepo.SetMediaFile(
file,
filename,
size,
collection,
)
if err != nil {
slog.Error("mediaService.mediaRepo.SetMediaFile: " + err.Error())
return err
}
err = mediaService.SetMediaUrl(
ctx,
mediaType,
widgetID,
model.MediaUrl(fileID),
)
if err != nil {
slog.Error("mediaService.SetMediaUrl: " + err.Error())
return err
}
return nil
}
func (mediaService *ServiceMedia) GetMediaFile(
ctx context.Context,
widgetID model.WidgetID,
mediaType model.MediaType,
) (model.DownloadFile, error) {
fileID, err := mediaService.mediaRepo.GetMediaUrl(
ctx,
widgetID,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.GetMediaUrl: " + err.Error())
return nil, err
}
file, err := mediaService.mediaRepo.GetMediaFile(
model.FileID(fileID),
)
if err != nil {
slog.Error("mediaService.mediaRepo.GetMediaFile: " + err.Error())
return nil, err
}
return file, nil
}
func (mediaService *ServiceMedia) UpdateMediaFile(
ctx context.Context,
widgetID model.WidgetID,
mediaType model.MediaType,
file model.UploadFile,
filename string,
size int64,
collection string,
) error {
fileID, err := mediaService.mediaRepo.GetMediaUrl(
ctx,
widgetID,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.GetMediaUrl: " + err.Error())
return err
}
err = mediaService.mediaRepo.UpdateMediaFile(
ctx,
widgetID,
file,
model.FileID(fileID),
filename,
size,
collection,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.UpdateMediaFile: " + err.Error())
return err
}
return nil
}
func (mediaService *ServiceMedia) SetMediaUrl(
ctx context.Context,
mediaType model.MediaType,
widgetID model.WidgetID,
mediaUrl model.MediaUrl,
) error {
err := mediaService.mediaRepo.SetMediaUrl(
ctx,
widgetID,
mediaUrl,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.SetMediaUrl: " + err.Error())
return err
}
return nil
}

View File

@ -113,3 +113,117 @@ func (widgetService *ServiceWidget) GetWidgetInfo(
return &donatAndWidget, nil
}
func (widgetService *ServiceWidget) SetMediaFile(
ctx context.Context,
mediaType model.MediaType,
widgetID model.WidgetID,
file model.UploadFile,
filename string,
size int64,
collection string,
) error {
fileID, err := widgetService.widgetRepo.SetMediaFile(
file,
filename,
size,
collection,
)
if err != nil {
slog.Error("mediaService.mediaRepo.SetMediaFile: " + err.Error())
return err
}
err = widgetService.SetMediaUrl(
ctx,
mediaType,
widgetID,
model.MediaUrl(fileID),
)
if err != nil {
slog.Error("mediaService.SetMediaUrl: " + err.Error())
return err
}
return nil
}
func (widgetService *ServiceWidget) GetMediaFile(
ctx context.Context,
widgetID model.WidgetID,
mediaType model.MediaType,
) (model.DownloadFile, error) {
fileID, err := widgetService.widgetRepo.GetMediaUrl(
ctx,
widgetID,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.GetMediaUrl: " + err.Error())
return nil, err
}
file, err := widgetService.widgetRepo.GetMediaFile(
model.FileID(fileID),
)
if err != nil {
slog.Error("mediaService.mediaRepo.GetMediaFile: " + err.Error())
return nil, err
}
return file, nil
}
func (widgetService *ServiceWidget) UpdateMediaFile(
ctx context.Context,
widgetID model.WidgetID,
mediaType model.MediaType,
file model.UploadFile,
filename string,
size int64,
collection string,
) error {
fileID, err := widgetService.widgetRepo.GetMediaUrl(
ctx,
widgetID,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.GetMediaUrl: " + err.Error())
return err
}
err = widgetService.widgetRepo.UpdateMediaFile(
ctx,
widgetID,
file,
model.FileID(fileID),
filename,
size,
collection,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.UpdateMediaFile: " + err.Error())
return err
}
return nil
}
func (widgetService *ServiceWidget) SetMediaUrl(
ctx context.Context,
mediaType model.MediaType,
widgetID model.WidgetID,
mediaUrl model.MediaUrl,
) error {
err := widgetService.widgetRepo.SetMediaUrl(
ctx,
widgetID,
mediaUrl,
mediaType,
)
if err != nil {
slog.Error("mediaService.mediaRepo.SetMediaUrl: " + err.Error())
return err
}
return nil
}

View File

@ -0,0 +1,71 @@
package payment
import (
"bytes"
"donat-widget/internal/model"
"donat-widget/internal/model/api"
"encoding/json"
"errors"
"io"
"log/slog"
"net/http"
)
func New(host, port string) *ClientPayment {
return &ClientPayment{
client: &http.Client{},
baseURL: "http://" + host + ":" + port + "/api/payment",
}
}
type ClientPayment struct {
client *http.Client
baseURL string
}
func (c *ClientPayment) CreatePayment(
streamerID model.StreamerID,
amount model.DonatAmount,
orderID model.OrderID,
) (api.CreatePaymentResponse, error) {
requestBody := map[string]any{
"sellerID": streamerID,
"amount": amount,
"orderID": orderID,
}
response, err := c.post("/buyer/pay", requestBody)
if err != nil {
slog.Error("c.post /buyer/pay", err)
return api.CreatePaymentResponse{}, err
}
var createPaymentResponse api.CreatePaymentResponse
if err = json.Unmarshal(response, &createPaymentResponse); err != nil {
slog.Error("json.Unmarshal", err)
return api.CreatePaymentResponse{}, err
}
return createPaymentResponse, nil
}
func (c *ClientPayment) post(path string, body map[string]any) ([]byte, error) {
bytesBody, _ := json.Marshal(body)
resp, err := c.client.Post(c.baseURL+path, "application/json", bytes.NewReader(bytesBody))
if err != nil {
slog.Error("c.client.Post: " + err.Error())
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New("tinkoff: post failed: " + resp.Status)
}
response, err := io.ReadAll(resp.Body)
if err != nil {
slog.Error("io.ReadAll: " + err.Error())
return nil, err
}
return response, nil
}