mirror of
https://github.com/3ybactuk/marketplace-go-service-project.git
synced 2025-10-30 05:53:45 +03:00
[HW-1] added docs
This commit is contained in:
18
docs/README.md
Normal file
18
docs/README.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Домашние задания
|
||||||
|
|
||||||
|
## Введение
|
||||||
|
|
||||||
|
В рамках учебного проекта предстоит реализовать систему, состоящую из нескольких сервисов, которая
|
||||||
|
будет моделировать работу простого интернет магазина включая такие бизнес процессы как:
|
||||||
|
|
||||||
|
- добавление товаров в корзину и их удаление из нее
|
||||||
|
- просмотр содержимого корзины
|
||||||
|
- оформление заказа по текущему составу корзины
|
||||||
|
- создание заказа
|
||||||
|
- оплата заказа
|
||||||
|
- отмена заказа пользователем или по истечению времени ожидания оплаты
|
||||||
|
|
||||||
|
## Содержание
|
||||||
|
|
||||||
|
1. [Основы Go](./homework-1)
|
||||||
|
|
||||||
274
docs/homework-1/README.md
Normal file
274
docs/homework-1/README.md
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
# Домашнее задание по модулю "Основы Go"
|
||||||
|
|
||||||
|
Необходимо реализовать сервис для работы с корзиной пользователя Cart
|
||||||
|
|
||||||
|
## Основное задание
|
||||||
|
|
||||||
|
Необходимо имплементировать сервис, отвечающий за работу с корзиной пользователя (сервис Cart). Логика работы методов
|
||||||
|
и их контракты описаны ниже.
|
||||||
|
|
||||||
|
Требования к решению:
|
||||||
|
|
||||||
|
1. Используем HTTP, на основе стандартной библиотеки Go 1.23
|
||||||
|
2. Для определения существования товара делаем поход в сервис `products`
|
||||||
|
3. Состояние храним в in-memory, персистентное хранилище на данный момент не требуется
|
||||||
|
4. Никакого резерва стоков не делаем, логика простейшая
|
||||||
|
|
||||||
|
## Дополнительное задание
|
||||||
|
|
||||||
|
1. Делаем Middleware, который будет логировать поступающие запросы
|
||||||
|
2. Делаем валидацию входящих структур на основе любой Open Source библиотеки (можно подсмотреть
|
||||||
|
тут - https://awesome-go.com/validation/)
|
||||||
|
3. Делаем ретраи в `products` на 420/429 статус в виде Client Middleware. 3 ретрая, потом ошибка
|
||||||
|
|
||||||
|
## Спецификация
|
||||||
|
|
||||||
|
### Добавить товар в корзину
|
||||||
|
|
||||||
|
Идентификатором товара является числовой идентификатор SKU. Метод добавляет указанный товар в корзину
|
||||||
|
определенного пользователя. Каждый пользователь имеет числовой идентификатор userID. При добавлении в корзину
|
||||||
|
проверяем, что товар существует в специальном сервисе.
|
||||||
|
|
||||||
|
Один и тот же товар может быть добавлен в корзину несколько раз, при этом количество экземпляров складывается.
|
||||||
|
|
||||||
|
| Метод | URI |
|
||||||
|
|-------|-------------------------------------------------|
|
||||||
|
| POST | /user/<user_id>/cart/<sku_id> |
|
||||||
|
|
||||||
|
**Параметры запроса:**
|
||||||
|
|
||||||
|
| Параметр | Тип параметра | Тип данных | Пример | Описание |
|
||||||
|
|----------|---------------|------------|--------|------------------------------------------------------------------|
|
||||||
|
| user_id | query path | int64 | 1007 | Идентификатор пользователя, в корзину которого добавляется товар |
|
||||||
|
| sku_id | query path | int64 | 2008 | Идентификатор товара, добавляемого в корзину |
|
||||||
|
| count | body | uint32 | 12 | Количество товаров, добавляемое в корзину |
|
||||||
|
|
||||||
|
**Параметры ответа:**
|
||||||
|
|
||||||
|
отсутствуют
|
||||||
|
|
||||||
|
**Параметры ошибочных ответов:**
|
||||||
|
|
||||||
|
| Сценарий | HTTP код ошибки | Описание |
|
||||||
|
|-------------------------------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------|
|
||||||
|
| Добавление валидного SKU для пользователя (user_id) с нулевым или отрицательным значением | 400 | Идентификатор пользователя должен быть натуральным числом (больше нуля) |
|
||||||
|
| Добавление SKU с нулевым или отрицательным значением | 400 | SKU должен быть натуральным числом (больше нуля) |
|
||||||
|
| Добавление SKU с нулевым или отрицательным количеством (count) | 400 | Количество должно быть натуральным числом (больше нуля) |
|
||||||
|
| Добавление несуществующего SKU в корзину | 412 | SKU должен существовать в сервисе `products` |
|
||||||
|
|
||||||
|
**Диаграмма последовательности:**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Удалить товар из корзины
|
||||||
|
|
||||||
|
Метод полностью удаляет все количество товара из корзины пользователя. Если у пользователя вовсе нет данной позиции,
|
||||||
|
то возвращается такой же ответ, как будто бы все позиции данного sku были успешно удалены.
|
||||||
|
|
||||||
|
| Метод | URI |
|
||||||
|
|--------|-------------------------------------------------|
|
||||||
|
| DELETE | /user/<user_id>/cart/<sku_id> |
|
||||||
|
|
||||||
|
**Параметры запроса:**
|
||||||
|
|
||||||
|
| Параметр | Тип параметра | Тип данных | Пример | Описание |
|
||||||
|
|----------|---------------|------------|--------|------------------------------------------------------------------|
|
||||||
|
| user_id | query path | int64 | 1007 | Идентификатор пользователя, в корзину которого добавляется товар |
|
||||||
|
| sku_id | query path | int64 | 2008 | Идентификатор товара, удаляемого из корзины |
|
||||||
|
|
||||||
|
**Параметры ответа:**
|
||||||
|
|
||||||
|
отсутствуют
|
||||||
|
|
||||||
|
**Параметры ошибочных ответов:**
|
||||||
|
|
||||||
|
| Сценарий | HTTP код ошибки | Описание |
|
||||||
|
|------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------|
|
||||||
|
| Удаление SKU с нулевым или отрицательным значением | 400 | SKU должен быть натуральным числом (больше нуля) |
|
||||||
|
| Удаление SKU с нулевым или отрицательным пользователем (user_id) | 400 | Идентификатор пользователя должен быть натуральным числом (больше нуля) |
|
||||||
|
|
||||||
|
**Диаграмма последовательности:**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Очистить корзину пользователя
|
||||||
|
|
||||||
|
Метод полностью очищает корзину пользователя. Если у пользователя нет корзины или она пуста, то, как и при успешной
|
||||||
|
очистке корзины, необходимо вернуть код ответа 204 No Content.
|
||||||
|
|
||||||
|
| Метод | URI |
|
||||||
|
|--------|----------------------|
|
||||||
|
| DELETE | /user/<user_id>/cart |
|
||||||
|
|
||||||
|
**Параметры запроса:**
|
||||||
|
|
||||||
|
| Параметр | Тип параметра | Тип данных | Пример | Описание |
|
||||||
|
|----------|---------------|------------|--------|------------------------------------------------------------------|
|
||||||
|
| user_id | query path | int64 | 1007 | Идентификатор пользователя, в корзину которого добавляется товар |
|
||||||
|
|
||||||
|
**Параметры ответа:**
|
||||||
|
|
||||||
|
отсутствуют
|
||||||
|
|
||||||
|
**Параметры ошибочных ответов:**
|
||||||
|
|
||||||
|
| Сценарий | HTTP код ошибки | Описание |
|
||||||
|
|----------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------|
|
||||||
|
| Удаление корзины с нулевым или отрицательным пользователем (user_id) | 400 | Идентификатор пользователя должен быть натуральным числом (больше нуля) |
|
||||||
|
|
||||||
|
**Диаграмма последовательности:**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Получить содержимое корзины
|
||||||
|
|
||||||
|
Метод возвращает содержимое корзины пользователя на текущий момент. Если корзины у переданного пользователя нет,
|
||||||
|
либо она пуста, следует вернуть 404 код ответа. Товары в корзине упорядочены в порядке возрастания sku.
|
||||||
|
|
||||||
|
| Метод | URI |
|
||||||
|
|-------|--------------------------|
|
||||||
|
| GET | /user/<user_id>/cart |
|
||||||
|
|
||||||
|
**Параметры запроса:**
|
||||||
|
|
||||||
|
| Параметр | Тип параметра | Тип данных | Пример | Описание |
|
||||||
|
|----------|---------------|------------|--------|------------------------------------------------------------------|
|
||||||
|
| user_id | query path | int64 | 1007 | Идентификатор пользователя, в корзину которого добавляется товар |
|
||||||
|
|
||||||
|
**Параметры ответа:**
|
||||||
|
|
||||||
|
| Параметр | Тип данных | Пример | Описание |
|
||||||
|
|----------------|------------|--------------------------------------------------|----------------------------------------------|
|
||||||
|
| items[i].sku | int64 | 2008 | Идентификатор товара в корзине пользователя |
|
||||||
|
| items[i].name | string | "Гречка пропаренная, в пакетиках для варки, 400" | Наименование товара |
|
||||||
|
| items[i].count | uint32 | 10 | Количество единиц товара |
|
||||||
|
| items[i].price | uint32 | 16 | Стоимость единицы товара в условных единицах |
|
||||||
|
| total_price | uint32 | 160 | Суммарная стоимость всех товаров в корзине |
|
||||||
|
|
||||||
|
|
||||||
|
**Пример ответа:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"items" : [
|
||||||
|
{
|
||||||
|
"sku": 2958025,
|
||||||
|
"name": "Roxy Music. Stranded. Remastered Edition",
|
||||||
|
"count": 2,
|
||||||
|
"price": 1028
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sku": 773297411,
|
||||||
|
"name": "Кроссовки Nike JORDAN",
|
||||||
|
"count": 1,
|
||||||
|
"price": 2202
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_price": 4258
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Параметры ошибочных ответов:**
|
||||||
|
|
||||||
|
| Сценарий | HTTP код ошибки | Описание |
|
||||||
|
|--------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------|
|
||||||
|
| Запрос корзины с нулевым или отрицательным пользователем (user_id) | 400 | Идентификатор пользователя должен быть натуральным числом (больше нуля) |
|
||||||
|
|
||||||
|
|
||||||
|
**Диаграмма последовательности:**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Взаимодействие с Product service
|
||||||
|
|
||||||
|
Если, вызвав `make run-all`, развернуть деплоймент, swagger этого сервиса
|
||||||
|
можно увидеть локально по адресу: [http://localhost:8082/docs/](http://localhost:8082/docs/)
|
||||||
|
|
||||||
|
Сервис поддерживает следующие операции:
|
||||||
|
|
||||||
|
|
||||||
|
#### GET /product?count=10&start_after_sku=0
|
||||||
|
|
||||||
|
Эта операция имеет два необязательных параметра:
|
||||||
|
- `count` — сколько элементов вернуть и
|
||||||
|
- `start_after_sku` — после какого элемента начать вывод.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name string
|
||||||
|
price int32
|
||||||
|
sku int64
|
||||||
|
},
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### GET /product/<sku>
|
||||||
|
|
||||||
|
Эта операция выводит данные товара с заданным `sku`.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
name string
|
||||||
|
price int32
|
||||||
|
sku int64
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Обратите внимание, сервис `products` отдаёт цены в `int32` формате.
|
||||||
|
В JSON нет беззнаковых целых, поэтому `int32`.
|
||||||
|
Вам же, внутри своих сервисов, для совместимости их между собой
|
||||||
|
следует использовать беззнаковый тип `uint32`.
|
||||||
|
|
||||||
|
Также следует обратить внимание на то, что он выведен на `localhost:8082` лишь
|
||||||
|
для вашего удобства работы с данными.
|
||||||
|
|
||||||
|
Ваш сервис `cart`, запущенный докер-контейнером, сможет подключиться к `products`
|
||||||
|
по адресу `products:8082` — заниматься маршрутизацией будет сам докер.
|
||||||
|
|
||||||
|
Авторизация на запрос выполняется с помощью заголовка `X-API-KEY` и токена `testToken`.
|
||||||
|
|
||||||
|
## Makefile
|
||||||
|
|
||||||
|
В рамках данного задания необходимо имплементировать следующие таргеты:
|
||||||
|
|
||||||
|
- run-all — запускает сервисы. На данный момент их должно стать два:
|
||||||
|
- `products` — уже реализован
|
||||||
|
- `cart` — разрабатываемый вами в качестве домашнего задания
|
||||||
|
|
||||||
|
## Ожидаемый результат
|
||||||
|
|
||||||
|
- с помощью команды `make run-all` можно запустить приложение
|
||||||
|
- приложение слушает HTTP-запросы на порту 8080
|
||||||
|
- реализованы API-методы
|
||||||
|
- POST /user/<user_id>/cart/<sku_id>
|
||||||
|
- DELETE /user/<user_id>/cart/<sku_id>
|
||||||
|
- DELETE /user/<user_id>/cart
|
||||||
|
- GET /user/<user_id>/cart
|
||||||
|
- методы реализуют заявленную бизнес-логику
|
||||||
|
- методы валидируют запросы и отдают описанные в спецификации коды ошибок
|
||||||
|
- информация о пользователях и их состоянии корзины хранится в памяти приложения
|
||||||
|
- при рестарте приложения состояние системы теряется
|
||||||
|
|
||||||
|
## Автоматические проверки
|
||||||
|
Ваше решение должно проходить автоматические проверки:
|
||||||
|
|
||||||
|
- Компиляция
|
||||||
|
- Линтер
|
||||||
|
- Unit-тесты (если есть)
|
||||||
|
- Автотесты
|
||||||
|
|
||||||
|
Прохождение автоматических проверок влияет на итоговую оценку за домашнюю работу.
|
||||||
|
|
||||||
|
## Сценарии тестирования с примерами запроса (для тьютора)
|
||||||
|
|
||||||
|
Сценарий тестирования следует описать в [cart.http](./cart.http)
|
||||||
|
|
||||||
|
### Дедлайны сдачи и проверки задания:
|
||||||
|
- 24 мая 23:59 (сдача) / 27 мая, 23:59 (проверка)
|
||||||
86
docs/homework-1/cart.http
Normal file
86
docs/homework-1/cart.http
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
### add 1 sku to cart
|
||||||
|
POST http://localhost:8080/user/31337/cart/1076963
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
### expected {} 200 OK; must add 1 item
|
||||||
|
|
||||||
|
### add 5 sku to cart
|
||||||
|
POST http://localhost:8080/user/31337/cart/1076963
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 5
|
||||||
|
}
|
||||||
|
### expected {} 200 OK; must add 5 more item, 1076963 - must be 6 items
|
||||||
|
|
||||||
|
### add unknown sku to cart
|
||||||
|
POST http://localhost:8080/user/31337/cart/1076963000
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
### expected {} 412 Precondition Failed; invalid sku
|
||||||
|
|
||||||
|
### add another sku to cart
|
||||||
|
POST http://localhost:8080/user/31337/cart/1148162
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
### expected {} 200 OK; must add 1 item
|
||||||
|
|
||||||
|
### invalid user
|
||||||
|
POST http://localhost:8080/user/0/cart/1148162
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
### expected {} 400 Bad Request
|
||||||
|
|
||||||
|
### invalid sku
|
||||||
|
POST http://localhost:8080/user/31337/cart/0
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
### expected {} 400 Bad Request
|
||||||
|
|
||||||
|
### invalid count
|
||||||
|
POST http://localhost:8080/user/31337/cart/1148162
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 0
|
||||||
|
}
|
||||||
|
### expected {} 400 Bad Request
|
||||||
|
|
||||||
|
# ========================================================================================
|
||||||
|
|
||||||
|
### delete whole sku from cart
|
||||||
|
DELETE http://localhost:8080/user/31337/cart/1076963
|
||||||
|
Content-Type: application/json
|
||||||
|
### expected {} 200 OK; must delete item from cart
|
||||||
|
|
||||||
|
### delete whole cart
|
||||||
|
DELETE http://localhost:8080/user/31337/cart
|
||||||
|
Content-Type: application/json
|
||||||
|
### expected {} 204 No Content; must delete cart
|
||||||
|
|
||||||
|
# ========================================================================================
|
||||||
|
|
||||||
|
### get list of a cart
|
||||||
|
GET http://localhost:8080/user/31337/cart
|
||||||
|
Content-Type: application/json
|
||||||
|
### expected {} 200 OK; must show cart
|
||||||
|
|
||||||
|
### get invalid list of cart
|
||||||
|
GET http://localhost:8080/user/0/cart
|
||||||
|
Content-Type: application/json
|
||||||
|
### 400 bad request
|
||||||
5
docs/homework-1/http-client.env.example.json
Normal file
5
docs/homework-1/http-client.env.example.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dev": {
|
||||||
|
"port": "8080"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
docs/homework-1/img/cart-cart-clear.plantuml
Normal file
13
docs/homework-1/img/cart-cart-clear.plantuml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@startuml
|
||||||
|
|
||||||
|
actor User as u
|
||||||
|
collections Cart as c
|
||||||
|
database CartStorage as cs
|
||||||
|
|
||||||
|
u -> c : DELETE /user/<user_id>/cart
|
||||||
|
activate c
|
||||||
|
c -> cs : cart.DeleteItemsByUserID
|
||||||
|
c -> u : Response: 204 No Content
|
||||||
|
deactivate c
|
||||||
|
|
||||||
|
@enduml
|
||||||
BIN
docs/homework-1/img/cart-cart-clear.png
Normal file
BIN
docs/homework-1/img/cart-cart-clear.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
21
docs/homework-1/img/cart-cart-item-add.plantuml
Normal file
21
docs/homework-1/img/cart-cart-item-add.plantuml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
@startuml
|
||||||
|
|
||||||
|
actor User as u
|
||||||
|
collections Cart as c
|
||||||
|
database CartStorage as cs
|
||||||
|
collections ProductService as p
|
||||||
|
|
||||||
|
u -> c : POST /user/<user_id>/cart/<sku_id>\n\t- count
|
||||||
|
activate c
|
||||||
|
|
||||||
|
c -> p : GET /product/<sku>
|
||||||
|
activate p
|
||||||
|
p -> c : \nResponse: 200 OK\n\t- name\n\t- price\n\t- sku
|
||||||
|
deactivate p
|
||||||
|
c -> c : validate product exists
|
||||||
|
c -> cs : cart.AddItem()
|
||||||
|
c -> u : Response: 200 OK
|
||||||
|
|
||||||
|
deactivate c
|
||||||
|
|
||||||
|
@enduml
|
||||||
BIN
docs/homework-1/img/cart-cart-item-add.png
Normal file
BIN
docs/homework-1/img/cart-cart-item-add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
13
docs/homework-1/img/cart-cart-item-delete.plantuml
Normal file
13
docs/homework-1/img/cart-cart-item-delete.plantuml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@startuml
|
||||||
|
|
||||||
|
actor User as u
|
||||||
|
collections Cart as c
|
||||||
|
database CartStorage as cs
|
||||||
|
|
||||||
|
u -> c : DELETE /user/<user_id>/cart/<sku_id>
|
||||||
|
activate c
|
||||||
|
c -> cs : cart.DeleteItem(userID, skuID)
|
||||||
|
c -> u : Response: 204 No Content
|
||||||
|
deactivate c
|
||||||
|
|
||||||
|
@enduml
|
||||||
BIN
docs/homework-1/img/cart-cart-item-delete.png
Normal file
BIN
docs/homework-1/img/cart-cart-item-delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
25
docs/homework-1/img/cart-cart-list.plantuml
Normal file
25
docs/homework-1/img/cart-cart-list.plantuml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
@startuml
|
||||||
|
|
||||||
|
actor User as u
|
||||||
|
collections Cart as c
|
||||||
|
database CartStorage as cs
|
||||||
|
collections ProductService as p
|
||||||
|
|
||||||
|
u -> c : GET /user/<user_id>/cart
|
||||||
|
activate c
|
||||||
|
c -> cs : cart.GetItemsByUserID
|
||||||
|
alt cart exists
|
||||||
|
loop for each item in cart
|
||||||
|
c -> p : GET /product/<sku>
|
||||||
|
activate p
|
||||||
|
p -> c : Response: 200 OK\n\t- name\n\t- price\n\t- sku
|
||||||
|
deactivate p
|
||||||
|
c -> c : calculate total price
|
||||||
|
end
|
||||||
|
c -> u : Response: 200 OK\n\t- []item\n\t- total_price
|
||||||
|
else
|
||||||
|
c -> u : Response: 404 Not Found
|
||||||
|
end
|
||||||
|
deactivate c
|
||||||
|
|
||||||
|
@enduml
|
||||||
BIN
docs/homework-1/img/cart-cart-list.png
Normal file
BIN
docs/homework-1/img/cart-cart-list.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Reference in New Issue
Block a user