feat: create ads

This commit is contained in:
ericprd 2025-03-09 00:47:24 +08:00
parent 13c976dcbf
commit 213d370332
14 changed files with 289 additions and 0 deletions

11
database/ads_model.go Normal file
View File

@ -0,0 +1,11 @@
package database
import "time"
type Ads struct {
ID string `gorm:"primaryKey;not null" json:"id"`
ImageUrl string `gorm:"not null" json:"image_url"`
Url string `gorm:"not null" json:"url"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}

View File

@ -54,5 +54,6 @@ func (db *DB) Migrate() error {
&News{},
&Tag{},
&Category{},
&Ads{},
)
}

View File

@ -0,0 +1,14 @@
package adsrepository
import (
"fmt"
adsdomain "legalgo-BE-go/internal/domain/ads"
)
func (a *accessor) Create(spec adsdomain.Ads) error {
if err := a.db.Create(&spec).Error; err != nil {
return fmt.Errorf("failed to create ads: %v", err)
}
return nil
}

View File

@ -0,0 +1,20 @@
package adsrepository
import (
"legalgo-BE-go/database"
adsdomain "legalgo-BE-go/internal/domain/ads"
)
type accessor struct {
db *database.DB
}
type Ads interface {
Create(adsdomain.Ads) error
}
func New(
db *database.DB,
) Ads {
return &accessor{db}
}

View File

@ -1,6 +1,7 @@
package repository
import (
adsrepository "legalgo-BE-go/internal/accessor/ads"
categoryrepository "legalgo-BE-go/internal/accessor/category"
newsrepository "legalgo-BE-go/internal/accessor/news"
"legalgo-BE-go/internal/accessor/oss"
@ -24,4 +25,5 @@ var Module = fx.Module("repository", fx.Provide(
categoryrepository.New,
newsrepository.New,
oss.New,
adsrepository.New,
))

View File

@ -68,3 +68,19 @@ func (r *OssRepositoryImpl) GetPublicURL(fileName string) string {
}
return fmt.Sprintf("%s:%s%s", r.cfg.GetPublicURL(), r.cfg.GetBucketName(), fileName)
}
func (r *OssRepositoryImpl) DeleteObject(ctx context.Context, fileName string) error {
if fileName == "" {
return fmt.Errorf("file name is not provided")
}
_, err := r.s3.DeleteObject(&s3.DeleteObjectInput{
Key: aws.String(fileName),
})
if err != nil {
return fmt.Errorf("failed to delete object %s from bucket: %v", fileName, err)
}
return nil
}

View File

@ -7,4 +7,5 @@ import (
type OSSRepository interface {
UploadFile(ctx context.Context, fileName string, fileContent []byte) (fileUrl string, err error)
GetPublicURL(fileName string) string
DeleteObject(ctx context.Context, fileName string) error
}

View File

@ -0,0 +1,106 @@
package adshttp
import (
"fmt"
authmiddleware "legalgo-BE-go/internal/api/http/middleware/auth"
adsdomain "legalgo-BE-go/internal/domain/ads"
"legalgo-BE-go/internal/domain/oss"
adssvc "legalgo-BE-go/internal/services/ads"
"legalgo-BE-go/internal/utilities/response"
"net/http"
"github.com/go-chi/chi/v5"
)
const _oneMB = 1 << 20
const _maxUploadSize = 2 * _oneMB
const _folderName = "/file"
func Create(
router chi.Router,
adsSvc adssvc.Ads,
) {
router.With(authmiddleware.Authorize()).Post("/ads/create", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
err := r.ParseMultipartForm(10 << 20) // 10 MB
if err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"unable to parse form",
)
return
}
url := r.FormValue("url")
if url == "" {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"URL is missing",
)
return
}
file, header, err := r.FormFile("image")
if err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"unable to retrieve image",
)
return
}
defer file.Close()
if header.Size > int64(_maxUploadSize) {
response.ResponseWithErrorCode(
ctx,
w,
fmt.Errorf("file too big"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
fmt.Sprintf("The file is too big. The maximum size is %d MB", _maxUploadSize/_oneMB),
)
return
}
imageRequest := &oss.UploadFileRequest{
FileHeader: header,
FolderName: _folderName,
}
adsReq := adsdomain.AdsReq{
Image: imageRequest,
Url: url,
}
if err := adsSvc.Create(ctx, adsReq); err != nil {
response.RespondJsonErrorWithCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
response.RespondJsonSuccess(ctx, w, struct {
Message string
}{
Message: "ads created successfully",
})
})
}

View File

@ -0,0 +1,7 @@
package adshttp
import "go.uber.org/fx"
var Module = fx.Module("ads-http", fx.Invoke(
Create,
))

View File

@ -1,6 +1,7 @@
package internalhttp
import (
adshttp "legalgo-BE-go/internal/api/http/ads"
categoryhttp "legalgo-BE-go/internal/api/http/category"
newshttp "legalgo-BE-go/internal/api/http/news"
osshttp "legalgo-BE-go/internal/api/http/oss"
@ -31,6 +32,7 @@ var Module = fx.Module("router",
osshttp.Module,
userhttp.Module,
subscribehttp.Module,
adshttp.Module,
)
func initRouter() chi.Router {

View File

@ -0,0 +1,19 @@
package adsdomain
import (
"legalgo-BE-go/internal/domain/oss"
"time"
)
type Ads struct {
ID string `gorm:"primaryKey;not null" json:"id"`
ImageUrl string `gorm:"not null" json:"image_url"`
Url string `gorm:"not null" json:"url"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
type AdsReq struct {
Image *oss.UploadFileRequest `json:"image"`
Url string `json:"url"`
}

View File

@ -0,0 +1,56 @@
package adssvc
import (
"context"
"fmt"
adsdomain "legalgo-BE-go/internal/domain/ads"
"path"
"github.com/google/uuid"
)
func (i *impl) Create(ctx context.Context, spec adsdomain.AdsReq) error {
id := uuid.NewString()
file := spec.Image.FileHeader
spec.Image.FileSize = file.Size
spec.Image.Ext = path.Ext(file.Filename)
if err := i.validate.Struct(spec); err != nil {
return err
}
// Open the file and read its content
srcFile, err := file.Open()
if err != nil {
return err
}
defer srcFile.Close()
fileContent := make([]byte, file.Size)
_, err = srcFile.Read(fileContent)
if err != nil {
return err
}
filePath := fmt.Sprintf("%v/%v%v", spec.Image.FolderName, id, spec.Image.Ext)
fileUrl, err := i.ossRepo.UploadFile(ctx, filePath, fileContent)
if err != nil {
return err
}
newSpec := adsdomain.Ads{
ID: id,
ImageUrl: fileUrl,
Url: spec.Url,
}
if err := i.ads.Create(newSpec); err != nil {
if err := i.ossRepo.DeleteObject(ctx, id); err != nil {
return err
}
return err
}
return nil
}

View File

@ -0,0 +1,32 @@
package adssvc
import (
"context"
adsrepository "legalgo-BE-go/internal/accessor/ads"
"legalgo-BE-go/internal/accessor/oss"
adsdomain "legalgo-BE-go/internal/domain/ads"
"github.com/go-playground/validator/v10"
)
type impl struct {
ossRepo oss.OSSRepository
ads adsrepository.Ads
validate *validator.Validate
}
type Ads interface {
Create(context.Context, adsdomain.AdsReq) error
}
func New(
ossRepo oss.OSSRepository,
ads adsrepository.Ads,
validate *validator.Validate,
) Ads {
return &impl{
ossRepo,
ads,
validate,
}
}

View File

@ -1,6 +1,7 @@
package services
import (
adssvc "legalgo-BE-go/internal/services/ads"
serviceauth "legalgo-BE-go/internal/services/auth"
categorysvc "legalgo-BE-go/internal/services/category"
newssvc "legalgo-BE-go/internal/services/news"
@ -23,5 +24,6 @@ var Module = fx.Module("services",
newssvc.New,
oss.NewOSSService,
usersvc.New,
adssvc.New,
),
)