Merge branch 'dev' into 'fastlinks'

# Conflicts:
#   docs/openapi.yaml
This commit is contained in:
Mikhail 2024-03-09 15:56:52 +00:00
commit f27b253156
5 changed files with 176 additions and 15 deletions

25
README.md Normal file

@ -0,0 +1,25 @@
# codeword
Это сервис обмена кодового слова на какие либо действия.
- Обмен кодового слова из ссылки, чтобы получить пару токенов и сменить пароль
- Обмен кодового слова промокода, чтобы создать персональную временную скидку
- Выполнение некоторой последовательности действий по нажатию на ссылку
Вот общая суть цель работы сервиса.
# Конкретные сценарии
## Восстановление пароля
Для того чтобы восстановить пароль, надо следующее:
- Сгенерировать ключ при помощи шифрования на эллиптических кривых
- По email, переданному в запросе, найти пользователя, для которого будем восстанавливать пароль
- Сложить в базу запись с подписью, айдишником, урлом перенаправления и датой создания
- Отправить на этот email письмо с ссылкой типа https://hub.pena.digital/codeword/restore/{signId}
- В обработчике таких ссылок получить из роута sign
- По ней найти запись, сложенную выше
- Если с момента создания прошло больше 15 минут, то вернуть ошибку
- Иначе, сходить на auth сервис на метод exchange, с которого получить пару токенов, которые вернуть запрашивающему

@ -268,9 +268,48 @@ components:
properties:
promoCodeID:
type: string
required:
- promoCodeID
PromoCodeStatsResp:
description: Кодовое слово, которое должен ввести пользователь
description:
type: string
description: Описание, необходимое для администратора в панели управления
greetings:
type: string
description: Текст, который будет отправлен пользователю в ответ на активацию кода
dueTo:
type: integer
format: int64
description: Временная метка окончания активации кода
activationCount:
type: integer
format: int64
description: Лимит активации кода
bonus:
type: object
properties:
privilege:
type: object
properties:
privilegeID:
type: string
description: Идентификатор привилегии, которую необходимо предоставить
amount:
type: integer
format: uint64
description: Размер привилегии
discount:
type: object
properties:
layer:
type: integer
factor:
type: number
target:
type: string
threshold:
type: integer
description: Информация о бонусах
PromoCodeResponse:
type: object
properties:
id:
@ -303,9 +342,65 @@ components:
properties:
id:
type: string
description: ID промокода, для которого нужно создать быструю ссылку
description: ID промокода, который обновляем
description:
type: string
description: Описание, необходимое менеджеру в админке
greetings:
type: string
description: Текст, выдаваемый пользователю в ответ на активацию промокода
dueTo:
type: integer
format: int64
description: Временная метка окончания активации кода
activationCount:
type: integer
format: int64
description: Предел количества активаций промокода
delete:
type: boolean
description: Флаг удаления промокода
bonus:
type: object
properties:
privilege:
type: object
properties:
privilegeID:
type: string
description: Идентификатор привилегии, которую необходимо предоставить
amount:
type: integer
format: uint64
description: Размер привилегии
discount:
type: object
properties:
layer:
type: integer
factor:
type: number
target:
type: string
threshold:
type: integer
description: Информация о бонусах
required:
- id
GetPromoCodesListReq:
type: object
required:
- page
- limit
properties:
page:
type: integer
description: Номер страницы выборки, начиная с 0
limit:
type: integer
description: Размер страницы выборки
filter:
$ref: '#/components/schemas/GetPromoCodesListReqFilter'
CreateFastLinkResp:
type: object

@ -89,11 +89,10 @@ func (p *PromoCodeController) GetList(c *fiber.Ctx) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
resp := models.GetPromoCodesListResp{
return c.Status(fiber.StatusOK).JSON(models.GetPromoCodesListResp{
Count: count,
Items: promoCodes,
}
return c.Status(fiber.StatusOK).JSON(resp)
})
}
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {

@ -32,7 +32,7 @@ type PromoCode struct {
}
type ReqEditPromoCode struct {
ID string `json:"id" bson:"_id"` //айдишник промокода, который обновляем
ID string `json:"id" bson:"_id"` // айдишник промокода, который обновляем
Description *string `json:"description,omitempty" bson:"description"` // описание, необходимое менеджеру в админке
Greetings *string `json:"greetings,omitempty" bson:"greetings"` // текст, выдаваемый пользователю в ответ на активацию промокода
@ -40,6 +40,20 @@ type ReqEditPromoCode struct {
ActivationCount *int64 `json:"activationCount,omitempty" bson:"activationCount"` // предел количества активаций промокода
Delete *bool `json:"delete,omitempty" bson:"delete"`
Bonus *struct {
Privilege *struct {
PrivilegeID string `json:"privilegeID,omitempty" bson:"privilegeID"`
Amount uint64 `json:"amount,omitempty" bson:"amount"`
} `json:"privilege,omitempty" bson:"privilege"`
Discount *struct {
Layer int `json:"layer,omitempty" bson:"layer"`
Factor float64 `json:"factor,omitempty" bson:"factor"`
Target string `json:"target,omitempty" bson:"target"`
Threshold int64 `json:"threshold,omitempty" bson:"threshold"`
} `json:"discount,omitempty" bson:"discount"`
} `json:"bonus,omitempty" bson:"bonus"`
}
type GetPromoCodesListReqFilter struct {

@ -99,21 +99,49 @@ func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, req *models.Req
updateFields["delete"] = *req.Delete
}
if req.Bonus != nil {
if req.Bonus.Privilege != nil {
if req.Bonus.Privilege.PrivilegeID != "" {
updateFields["bonus.privilege.privilegeID"] = req.Bonus.Privilege.PrivilegeID
}
if req.Bonus.Privilege.Amount != 0 {
updateFields["bonus.privilege.amount"] = req.Bonus.Privilege.Amount
}
}
if req.Bonus.Discount != nil {
if req.Bonus.Discount.Layer != 0 {
updateFields["bonus.discount.layer"] = req.Bonus.Discount.Layer
}
if req.Bonus.Discount.Factor != 0.0 {
updateFields["bonus.discount.factor"] = req.Bonus.Discount.Factor
}
if req.Bonus.Discount.Target != "" {
updateFields["bonus.discount.target"] = req.Bonus.Discount.Target
}
if req.Bonus.Discount.Threshold != 0 {
updateFields["bonus.discount.threshold"] = req.Bonus.Discount.Threshold
}
}
}
if len(updateFields) == 0 {
return r.GetPromoCodeByID(ctx, promoCodeID)
}
update := bson.M{"$set": updateFields}
result, err := r.mdb.UpdateOne(ctx, bson.M{"_id": promoCodeID}, update)
options := options.FindOneAndUpdate().SetReturnDocument(options.After)
result := r.mdb.FindOneAndUpdate(ctx, bson.M{"_id": promoCodeID}, update, options)
if result.Err() != nil {
return nil, result.Err()
}
var updatedPromoCode models.PromoCode
err = result.Decode(&updatedPromoCode)
if err != nil {
return nil, err
}
if result.MatchedCount == 0 {
return nil, ErrPromoCodeNotFound
}
return r.GetPromoCodeByID(ctx, promoCodeID)
return &updatedPromoCode, nil
}
func (r *PromoCodeRepository) GetPromoCodeByID(ctx context.Context, promoCodeID primitive.ObjectID) (*models.PromoCode, error) {
@ -164,7 +192,7 @@ func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models
}
defer cursor.Close(ctx)
var promoCodes = make([]models.PromoCode, 0)
var promoCodes = make([]models.PromoCode, 0, 10)
for cursor.Next(ctx) {
var p models.PromoCode
if err := cursor.Decode(&p); err != nil {