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:
|
properties:
|
||||||
promoCodeID:
|
promoCodeID:
|
||||||
type: string
|
type: string
|
||||||
required:
|
description: Кодовое слово, которое должен ввести пользователь
|
||||||
- promoCodeID
|
description:
|
||||||
PromoCodeStatsResp:
|
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
|
type: object
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
@ -303,9 +342,65 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: string
|
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:
|
required:
|
||||||
- id
|
- id
|
||||||
|
GetPromoCodesListReq:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- page
|
||||||
|
- limit
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
description: Номер страницы выборки, начиная с 0
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
description: Размер страницы выборки
|
||||||
|
filter:
|
||||||
|
$ref: '#/components/schemas/GetPromoCodesListReqFilter'
|
||||||
|
|
||||||
CreateFastLinkResp:
|
CreateFastLinkResp:
|
||||||
type: object
|
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"})
|
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,
|
Count: count,
|
||||||
Items: promoCodes,
|
Items: promoCodes,
|
||||||
}
|
})
|
||||||
return c.Status(fiber.StatusOK).JSON(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
||||||
|
@ -32,7 +32,7 @@ type PromoCode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ReqEditPromoCode struct {
|
type ReqEditPromoCode struct {
|
||||||
ID string `json:"id" bson:"_id"` //айдишник промокода, который обновляем
|
ID string `json:"id" bson:"_id"` // айдишник промокода, который обновляем
|
||||||
Description *string `json:"description,omitempty" bson:"description"` // описание, необходимое менеджеру в админке
|
Description *string `json:"description,omitempty" bson:"description"` // описание, необходимое менеджеру в админке
|
||||||
Greetings *string `json:"greetings,omitempty" bson:"greetings"` // текст, выдаваемый пользователю в ответ на активацию промокода
|
Greetings *string `json:"greetings,omitempty" bson:"greetings"` // текст, выдаваемый пользователю в ответ на активацию промокода
|
||||||
|
|
||||||
@ -40,6 +40,20 @@ type ReqEditPromoCode struct {
|
|||||||
ActivationCount *int64 `json:"activationCount,omitempty" bson:"activationCount"` // предел количества активаций промокода
|
ActivationCount *int64 `json:"activationCount,omitempty" bson:"activationCount"` // предел количества активаций промокода
|
||||||
|
|
||||||
Delete *bool `json:"delete,omitempty" bson:"delete"`
|
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 {
|
type GetPromoCodesListReqFilter struct {
|
||||||
|
@ -99,21 +99,49 @@ func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, req *models.Req
|
|||||||
updateFields["delete"] = *req.Delete
|
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 {
|
if len(updateFields) == 0 {
|
||||||
return r.GetPromoCodeByID(ctx, promoCodeID)
|
return r.GetPromoCodeByID(ctx, promoCodeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
update := bson.M{"$set": updateFields}
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.MatchedCount == 0 {
|
return &updatedPromoCode, nil
|
||||||
return nil, ErrPromoCodeNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.GetPromoCodeByID(ctx, promoCodeID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PromoCodeRepository) GetPromoCodeByID(ctx context.Context, promoCodeID primitive.ObjectID) (*models.PromoCode, error) {
|
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)
|
defer cursor.Close(ctx)
|
||||||
|
|
||||||
var promoCodes = make([]models.PromoCode, 0)
|
var promoCodes = make([]models.PromoCode, 0, 10)
|
||||||
for cursor.Next(ctx) {
|
for cursor.Next(ctx) {
|
||||||
var p models.PromoCode
|
var p models.PromoCode
|
||||||
if err := cursor.Decode(&p); err != nil {
|
if err := cursor.Decode(&p); err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user