Merge branch 'dev' into 'fastlinks'
# Conflicts: # docs/openapi.yaml
This commit is contained in:
commit
f27b253156
25
README.md
Normal file
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user