تیم *انبوهفروشان مشرقزمین* قصد دارد سامانهای تحتوب برای مدیریت آسانتر فروشگاهشان راهاندازی کند. آنها تصمیم گرفتهاند تا وبسرویسشان را با استفاده از *Spring Boot* پیادهسازی کنند. نیازمندیها از قبل بهطور دقیق مشخص شدهاند، اما هنوز برنامهنویسی به تیم آنها اضافه نشده است. از شما میخواهیم تا نسخهی اولیهی این وبسرویس را پیادهسازی کنید.
# جزئیات پروژه
پروژهی اولیه را از  [این لینک](/problemset/assignments/4367/download_problem_initial_project/111985/)  دانلود کنید.
+ این پروژه از *Maven* استفاده میکند و وابستگیها در *POM* تعریف شدهاند. امکان تغییر این وابستگیها وجود ندارد.
+ از *[Liquibase](https://www.liquibase.org/)* برای مدیریت *schema* دیتابیس استفاده شده است.
+ برای اجرای تستها، از دیتابیس *[H2](https://www.h2database.com/)* استفاده میشود.
+ در این پروژه، وابستگی *[Project Lombok](https://projectlombok.org/)* تعریف شده و قابل استفاده است.
+ بستهی `ir.digipay.bulkshop` شامل موجودیتهای *JPA* برنامه است که مطابق با *schema* دیتابیس طراحی شدهاند. شناسه (`id`) موجودیتها بهصورت خودکار توسط *Hibernate* تولید میشود.
  + کلاس `CustomerEntity`: از این کلاس برای نگهداری اطلاعات هر مشتری استفاده میشود.
  + کلاس `ProductEntity`: از این کلاس برای نگهداری اطلاعات هر محصول استفاده میشود.
  + کلاس `OrderEntity`: از این کلاس برای نگهداری اطلاعات هر سفارش استفاده میشود.
+ بستهی `ir.digipay.bulkshop.api` شامل کلاسهایی برای مدیریت ورودیها و خروجیهای *API* است.
+ بستهی `ir.digipay.bulkshop.rest` شامل *REST controller* های برنامه است. کنترلر مربوط به آدرس `/` برای تست برنامه در کلاس `IndexRestEnpoint` پیادهسازی شده است. سایر کنترلرها را در این بسته پیادهسازی کنید.
+ از آنجا که این برنامه اولین نسخهی سامانه است، هر سفارش میتواند شامل یک نوع محصول باشد، اما تعداد آن را میتوان در سفارش تعیین کرد.
## سرویسها
تعدادی وبسرویس *REST* مطابق نیازمندیهای زیر باید پیادهسازی شود:
| آدرس                      |                   عنوان |
| :------------------------ | ----------------------: |
| `GET /api/products`       |  دریافت اطلاعات محصولات |
| `POST /api/products`      |             ایجاد محصول |
| `GET /api/products/{id}`  | دریافت اطلاعات یک محصول |
| `PUT /api/products`       | ویرایش اطلاعات یک محصول |
| `GET /api/customers`      |  دریافت اطلاعات مشتریان |
| `POST /api/customers`     |             ایجاد مشتری |
| `GET /api/customers/{id}` | دریافت اطلاعات یک مشتری |
| `PUT /api/customers`      | ویرایش اطلاعات یک مشتری |
| `GET /api/orders`         |  دریافت اطلاعات سفارشات |
| `POST /api/orders`        |             ایجاد سفارش |
| `GET /api/orders/{id}`    | دریافت اطلاعات یک سفارش |
### دریافت اطلاعات محصولات
اطلاعات همهی محصولات در قالب یک آرایه برگردانده میشود:
```yml
URL: "GET /api/products"
http-code: 200
content-type: "application/json"
response: {
  "products": [ # Array of ProductModel or ProductEntity
    {
      "id": "some_uuid",
      "name": "x",
      "price": 50000,
      "availableCount": 100
    },
    {
      "id": "some_uuid",
      "name": "y",
      "price": 75000,
      "availableCount": 50
    }
  ]
}
```
### ایجاد محصول
یک محصول با اطلاعات اولیهی دادهشده ایجاد میشود و شناسهی آن برگردانده میشود:
```yml
URL: "POST /api/products"
request: { # ProductCreateParam
  "name": "x",
  "price": 40000,
  "initialAvailableCount": 100
}
http-code: 201
content-type: "application/json"
response: { # ProductCreateResult
  "productId": "some_uuid"
}
```
### دریافت اطلاعات یک محصول
شناسهی محصول بهعنوان ورودی داده میشود و اطلاعات محصول برگردانده میشود:
```yml
URL: "GET /api/products/1234"
http-code: 200
content-type: "application/json"
response: { # ProductModel or ProductEntity
  "id": "some_uuid",
  "name": "z",
  "price": 40000,
  "availableCount": 150
}
```
اگر محصولی با شناسهی واردشده موجود نباشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 404
content-type: "application/json"
response: { 
  "message": "Entity not found"
}
```
### ویرایش اطلاعات یک محصول
اطلاعات جدید محصول داده میشود و پس از بهروزرسانی اطلاعات محصول، پاسخی با کد `204` برگردانده میشود:
```yml
URL: "PUT /api/products"
request: { # ProductUpdateParam
  "id": "some_uuid",
  "name": "x2",
  "price": 51000,
  "availableCount": 200
}
http-code: 204
```
اگر محصولی با شناسهی واردشده موجود نباشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 404
content-type: "application/json"
response: { 
  "message": "Entity not found"
}
```
### دریافت اطلاعات مشتریان
اطلاعات همهی مشتریان در قالب یک آرایه برگردانده میشود:
```yml
URL: "GET /api/customers"
http-code: 200
content-type: "application/json"
response: {
  "customers": [ # Array of CustomerModel or CustomerEntity
    {
      "id": "some_uuid",
      "username": "x",
      "firstName": "x",
      "lastName": "x"
    },
    {
      "id": "some_uuid",
      "username": "y",
      "firstName": "y",
      "lastName": "y"
    }
  ]
}
```
### ایجاد مشتری
یک مشتری با اطلاعات اولیهی دادهشده ایجاد میشود و شناسهی آن برگردانده میشود:
```yml
URL: "POST /api/customers"
request: { # CustomerCreateParam
  "username": "z",
  "firstName": "Z",
  "lastName": "z"
}
http-code: 201
content-type: "application/json"
response: { # CustomerCreateResult
  "customerId": "some_uuid"
}
```
اگر مشتریای با نام کاربری دادهشده از قبل موجود باشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 409
content-type: "application/json"
response: { 
  "message": "Username already taken"
}
```
### دریافت اطلاعات یک مشتری
شناسهی مشتری بهعنوان ورودی داده میشود و اطلاعات مشتری برگردانده میشود:
```yml
URL: "GET /api/customers/1"
http-code: 200
content-type: "application/json"
response: { # CustomerModel or CustomerEntity
  "id": "some_uuid",
  "username": "x",
  "firstName": "x",
  "lastName": "x"
}
```
اگر مشتریای با شناسهی واردشده موجود نباشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 404
content-type: "application/json"
response: { 
  "message": "Entity not found"
}
```
### ویرایش اطلاعات یک مشتری
اطلاعات جدید مشتری داده میشود و پس از بهروزرسانی اطلاعات مشتری، پاسخی با کد `204` برگردانده میشود:
```yml
URL: "PUT /api/customers"
request: { # CustomerUpdateParam
  "id": "some_uuid",
  "username": "x2",
  "firstName": "x2",
  "lastName": "x2"
}
http-code: 204
```
اگر مشتریای با شناسهی واردشده موجود نباشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 404
content-type: "application/json"
response: { 
  "message": "Entity not found"
}
```
### دریافت اطلاعات سفارشات
اطلاعات همهی محصولات در قالب یک آرایه برگردانده میشود:
```yml
URL: "GET /api/orders"
http-code: 200
content-type: "application/json"
response: {
  "orders": [ # Array of OrderModel or OrderEntity
    {
      "id": "some_uuid",
      "customerId": "some_uuid",
      "productId": "some_uuid",
      "count": 4
    },
    {
      "id": "some_uuid",
      "customerId": "some_uuid",
      "productId": "some_uuid",
      "count": 3
    }
  ]
}
```
### ایجاد سفارش
یک سفارش با اطلاعات دادهشده ایجاد میشود، مقدار موجودی محصول به اندازهی تعداد واردشده کم میشود و شناسهی سفارش برگردانده میشود:
```yml
URL: "POST /api/orders"
request: { # OrderCreateParam
  "customerId": "some_uuid",
  "productId": "some_uuid",
  "count": 6
}
http-code: 201
content-type: "application/json"
response: { # OrderCreateResult
  "orderId": "some_uuid"
}
```
اگر مشتری یا محصولی با شناسهی واردشده موجود نباشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 404
content-type: "application/json"
response: { 
  "message": "Entity not found"
}
```
اگر مقدار فیلد `count` کوچکتر یا مساوی صفر باشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 400
content-type: "application/json"
response: { 
  "message": "Invalid count"
}
```
اگر مقدار `count` بزرگتر از موجودی فعلی محصول باشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 400
content-type: "application/json"
response: { 
  "message": "Not enough available count"
}
```
### دریافت اطلاعات یک سفارش
شناسهی سفارش بهعنوان ورودی داده میشود و اطلاعات سفارش برگردانده میشود:
```yml
URL: "GET /api/orders/1234"
http-code: 200
content-type: "application/json"
response: { # OrderModel or OrderEntity
  "id": "some_uuid",
  "customerId": "some_uuid",
  "productId": "some_uuid",
  "count": 5
}
```
اگر سفارشی با شناسهی واردشده موجود نباشد، پاسخ باید بهصورت زیر باشد:
```yml
http-code: 404
content-type: "application/json"
response: { 
  "message": "Entity not found"
}
```
# نکات
+ ممکن است چند درخواست همزمان برای خرید یک محصول به برنامه ارسال شود. این درخواستها باید بهدرستی مدیریت شوند.
+ شما تنها مجاز به اعمال تغییرات در بستهی `ir.digipay.bulkshop` هستید.
# آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشهی `src` پروژه را زیپ کرده و ارسال کنید. توجه داشته باشید که فقط تغییرات اعمالشده در پوشهی `src/main/java/ir.digipay.bulkshop` در نظر گرفته میشوند.