Merge branch 'promoList' into 'dev'

Promo list

See merge request pena-services/codeword!8
This commit is contained in:
Mikhail 2024-02-21 21:22:12 +00:00
commit fd8c986e22
6 changed files with 218 additions and 30 deletions

@ -165,6 +165,34 @@ paths:
error:
type: string
/promocode/getList:
post:
summary: Получить список промокодов
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/GetPromoCodesListReq'
responses:
'200':
description: Список промокодов и общее количество успешно получены
content:
application/json:
schema:
$ref: '#/components/schemas/GetPromoCodesListResp'
'400':
description: Неверный запрос из-за невалидных данных
content:
application/json:
schema:
type: object
properties:
error:
type: string
'500':
description: Внутренняя ошибка сервера
components:
schemas:
@ -298,3 +326,40 @@ components:
description: Флаг удаления промокода
required:
- id
GetPromoCodesListReq:
type: object
required:
- page
- limit
- filter
properties:
page:
type: integer
description: Номер страницы выборки, начиная с 0
limit:
type: integer
description: Размер страницы выборки
filter:
$ref: '#/components/schemas/GetPromoCodesListReqFilter'
GetPromoCodesListReqFilter:
type: object
properties:
text:
type: string
description: Полнотекстовый поиск по полям Codeword, Description, Greetings
active:
type: boolean
description: Если true, выбираются записи, где delete, outdated и offLimit равны false
GetPromoCodesListResp:
type: object
properties:
count:
type: integer
format: int64
description: Общее количество промокодов в выборке
items:
type: array
items:
$ref: '#/components/schemas/PromoCodeResponse'

@ -22,12 +22,12 @@ func NewPromoCodeController(logger *zap.Logger, promoCodeService *services.Promo
}
func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
var reqCreatePromoCode models.PromoCode
if err := c.BodyParser(&reqCreatePromoCode); err != nil {
var req models.PromoCode
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
}
createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &reqCreatePromoCode)
createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &req)
if err != nil {
p.logger.Error("Failed to create promocode", zap.Error(err))
@ -42,16 +42,16 @@ func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
}
func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
var reqEditPromoCode models.ReqEditPromoCode
if err := c.BodyParser(&reqEditPromoCode); err != nil {
var req models.ReqEditPromoCode
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
}
if reqEditPromoCode.ID == "" {
if req.ID == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "promocode ID is required"})
}
editedPromoCode, err := p.promoCodeService.EditPromoCode(c.Context(), &reqEditPromoCode)
editedPromoCode, err := p.promoCodeService.EditPromoCode(c.Context(), &req)
if err != nil {
p.logger.Error("Failed to edit promocode", zap.Error(err))
@ -64,3 +64,22 @@ func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(editedPromoCode)
}
func (p *PromoCodeController) GetList(c *fiber.Ctx) error {
var req models.GetPromoCodesListReq
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
}
promoCodes, count, err := p.promoCodeService.GetPromoCodesList(c.Context(), &req)
if err != nil {
p.logger.Error("Failed to retrieve promocode list", zap.Error(err))
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
resp := models.GetPromoCodesListResp{
Count: count,
Items: promoCodes,
}
return c.Status(fiber.StatusOK).JSON(resp)
}

@ -40,3 +40,19 @@ type ReqEditPromoCode struct {
Delete *bool `json:"delete,omitempty" bson:"delete"`
}
type GetPromoCodesListReqFilter struct {
Text string `json:"text"` // полнотекстовый поиск пo Codeword, Decription, Greetings полям
Active bool `json:"active"` // если true, то выбирать deleted==false && outdated== false && offlimit == false
}
type GetPromoCodesListReq struct {
Page int `json:"page"` //номер страницы выборки. начинается с 0. по сути, skip для выборки из mongodb
Limit int `json:"limit"` //размер страницы выборки. больше 10, меньше 250. отвечает за skip = page*limit, и за limit
Filter GetPromoCodesListReqFilter `json:"filter"`
}
type GetPromoCodesListResp struct {
Count int64 `json:"count"` // количество в выборке всего
Items []PromoCode `json:"items"` // "страница" промокодов
}

@ -16,19 +16,39 @@ var (
ErrPromoCodeNotFound = errors.New("promo code not found")
)
// структура для горутины чтобы ошибки не пропускать
type countResult struct {
count int64
err error
}
type PromoCodeRepository struct {
mdb *mongo.Collection
}
func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
indexModel := mongo.IndexModel{
// todo заменить паники вроде как в роде не круто их юзать
uniqueIndexModel := mongo.IndexModel{
Keys: bson.D{
{"codeword", 1},
{"delete", 1},
{Key: "codeword", Value: 1},
{Key: "delete", Value: 1},
},
Options: options.Index().SetUnique(true).SetPartialFilterExpression(bson.M{"delete": false}),
}
_, err := mdb.Indexes().CreateOne(context.Background(), indexModel)
_, err := mdb.Indexes().CreateOne(context.Background(), uniqueIndexModel)
if err != nil {
panic(err)
}
textIndexModel := mongo.IndexModel{
Keys: bson.D{
{Key: "codeword", Value: "text"},
{Key: "description", Value: "text"},
{Key: "greetings", Value: "text"},
},
Options: options.Index().SetName("TextIndex"),
}
_, err = mdb.Indexes().CreateOne(context.Background(), textIndexModel)
if err != nil {
panic(err)
}
@ -36,11 +56,11 @@ func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
return &PromoCodeRepository{mdb: mdb}
}
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, promoCode *models.PromoCode) (*models.PromoCode, error) {
promoCode.CreatedAt = time.Now()
promoCode.ID = primitive.NewObjectID()
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) {
req.CreatedAt = time.Now()
req.ID = primitive.NewObjectID()
_, err := r.mdb.InsertOne(ctx, promoCode)
_, err := r.mdb.InsertOne(ctx, req)
if err != nil {
if writeErr, ok := err.(mongo.WriteException); ok {
for _, writeError := range writeErr.WriteErrors {
@ -53,30 +73,30 @@ func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, promoCode *mo
return nil, err
}
return promoCode, nil
return req, nil
}
func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, editPromoCode *models.ReqEditPromoCode) (*models.PromoCode, error) {
promoCodeID, err := primitive.ObjectIDFromHex(editPromoCode.ID)
func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) {
promoCodeID, err := primitive.ObjectIDFromHex(req.ID)
if err != nil {
return nil, err
}
updateFields := bson.M{}
if editPromoCode.Description != nil {
updateFields["description"] = *editPromoCode.Description
if req.Description != nil {
updateFields["description"] = *req.Description
}
if editPromoCode.Greetings != nil {
updateFields["greetings"] = *editPromoCode.Greetings
if req.Greetings != nil {
updateFields["greetings"] = *req.Greetings
}
if editPromoCode.DueTo != nil {
updateFields["dueTo"] = *editPromoCode.DueTo
if req.DueTo != nil {
updateFields["dueTo"] = *req.DueTo
}
if editPromoCode.ActivationCount != nil {
updateFields["activationCount"] = *editPromoCode.ActivationCount
if req.ActivationCount != nil {
updateFields["activationCount"] = *req.ActivationCount
}
if editPromoCode.Delete != nil {
updateFields["delete"] = *editPromoCode.Delete
if req.Delete != nil {
updateFields["delete"] = *req.Delete
}
if len(updateFields) == 0 {
@ -109,3 +129,59 @@ func (r *PromoCodeRepository) GetPromoCodeByID(ctx context.Context, promoCodeID
return &promoCode, nil
}
func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) {
filter := bson.M{}
if req.Filter.Text != "" {
filter["$text"] = bson.M{"$search": req.Filter.Text}
}
if req.Filter.Active {
filter["delete"] = false
filter["outdated"] = false
filter["offLimit"] = false
} else {
filter["$or"] = []interface{}{
bson.M{"delete": true},
bson.M{"outdated": true},
bson.M{"offLimit": true},
}
}
opt := options.Find().SetSkip(int64(req.Page * req.Limit)).SetLimit(int64(req.Limit))
var countChan = make(chan countResult)
go func() {
defer close(countChan)
count, err := r.mdb.CountDocuments(ctx, filter)
countChan <- countResult{count, err}
}()
cursor, err := r.mdb.Find(ctx, filter, opt)
if err != nil {
return nil, 0, err
}
defer cursor.Close(ctx)
var promoCodes = make([]models.PromoCode, 0)
for cursor.Next(ctx) {
var p models.PromoCode
if err := cursor.Decode(&p); err != nil {
return nil, 0, err
}
promoCodes = append(promoCodes, p)
}
if err := cursor.Err(); err != nil {
return nil, 0, err
}
result := <-countChan
if result.err != nil {
return nil, 0, result.err
}
count := result.count
return promoCodes, count, nil
}

@ -59,6 +59,7 @@ func (s *Server) registerRoutes() {
s.app.Post("/promocode/create", s.PromoCodeController.CreatePromoCode)
s.app.Put("/promocode/edit", s.PromoCodeController.EditPromoCode)
s.app.Post("/promocode/getList", s.PromoCodeController.GetList)
//... other
}

@ -7,8 +7,9 @@ import (
)
type PromoCodeRepository interface {
CreatePromoCode(ctx context.Context, promoCode *models.PromoCode) (*models.PromoCode, error)
EditPromoCode(ctx context.Context, reqEditPromoCode *models.ReqEditPromoCode) (*models.PromoCode, error)
CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error)
EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error)
GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error)
}
type PromoDeps struct {
@ -47,3 +48,13 @@ func (s *PromoCodeService) EditPromoCode(ctx context.Context, req *models.ReqEdi
return editedPromoCode, nil
}
func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) {
promoCodes, count, err := s.promoCodeRepo.GetPromoCodesList(ctx, req)
if err != nil {
s.logger.Error("Failed to get list promocodes from database", zap.Error(err))
return nil, 0, err
}
return promoCodes, count, nil
}