تیم همکارانسیستم تصمیم گرفته سیستم ردیابی ساعات ورود و خروج خود را با قابلیت شخصیسازی برای تیم *HR* طراحی کند. همکاران که **بهشدت** شرکت **منظمی** است و روی نظم کارمندان خود نیز حساس است از شما میخواهد برای ورود و استخدام به این شرکت و کمک به تیم منابع انسانی، بکاند سیستم ترکینگ ورود و خروج برنامهنویسان برای تهیه گزارشها و محاسبه حقوقشان را پیادهسازی کنید.
# جزئیات پروژه
پروژه اولیه را از [این لینک](/contest/assignments/80435/download_problem_initial_project/275771/) دانلود کنید. ساختار پروژه به شکل زیر است:
```shell
.
├── app
│ ├── db.go
│ ├── handlers.go # TODO Implement
│ ├── models.go # TODO Implement
│ ├── repository.go # TODO Implement
│ └── server.go
├── go.mod
├── go.sum
├── main.go
├── test
│ └── sample_test.go
```
در این پروژه، شما باید قسمتهای مشخص شده با `// TODO` را پیادهسازی کنید. در نهایت شما از طریق اجرای فایل باینری `main.go` میتوانید پروژه خود را اجرا کنید.
## مدلهای پایگاهداده
ساختار جداول پروژه به شرح زیر باشد:
1- جدول `Programmer`
| Column Name | Type | Notes |
|:------------------:|:------------------:|:-----------:|
| `ID` | `uint` |`PRIMARY KEY AUTO_INCREMENT`|
| `Name` |`string` | Programmer's name |
| `CreatedAt` | `time.Time` | Creation timestamp |
| `UpdatedAt` | `time.Time` | Last update timestamp |
2- جدول `Attendance`
| Column Name | Type | Notes |
|:------------------:|:------------------:|:-----------:|
| `ID` | `uint` |`PRIMARY KEY AUTO_INCREMENT`|
| `ProgrammerID` | `uint` | Foreign key to `Programmer` table |
| `Date` | `time.Time` | Attendance date |
| `CheckIn` | `time.Time` | Check-in time |
| `CheckOut` | `time.Time` | Check-out time |
| `CreatedAt` | `time.Time` | Creation timestamp |
| `UpdatedAt` | `time.Time` | Last update timestamp |
## روابط
روابط بین مدلها باید به شرح زیر پیادهسازی شوند:
+ هر برنامهنویس میتواند دارای چندین رکورد ورود و خروج باشد.
+ هر رکورد ورود و خروج متعلق به یک برنامهنویس است.
+ هر برنامهنویس در هر روز فقط باید یک رکورد ورود و خروج در سیستم ثبت نماید.
### توابع `Marshal/Unmarshal` جیسون
این توابع باید تبدیل صحیح بین *JSON* و ساختارهای *Go* برای مدل `Attendance` و `AttendanceInput` را مدیریت کنند:
```go
func (a *Attendance) UnmarshalJSON(data []byte) error
func (a Attendance) MarshalJSON() ([]byte, error)
func (ai *AttendanceInput) UnmarshalJSON(data []byte) error
```
<details class="blue">
<summary>
متد `UnmarshalJSON` برای `Attendance`
</summary>
```go
func (a *Attendance) UnmarshalJSON(data []byte) error
```
+ دادههای *JSON* را به ساختار `Attendance` تبدیل میکند.
+ فیلدهای `date`، `check_in` و `check_out` از نوع رشته (`string`) دریافت میشوند و به نوع `time.Time` تبدیل میشوند.
+ فرمتهای مورد انتظار:
+ `date`: `YYYY-MM-DD`
+ `check_in`: `YYYY-MM-DD HH:MM:SS`
+ `check_out`: `YYYY-MM-DD HH:MM:SS`
+ در صورت خطا در تبدیل فرمتها، خطا برگردانده میشود.
**خطاهای ممکن:**
+ اگر `date` فرمت نامعتبر داشته باشد، خطای `parsing time` با پیام `expected YYYY-MM-DD` برگردانده میشود.
+ اگر `check_in` یا `check_out` فرمت نامعتبر داشته باشند، خطای `parsing time` با پیام `expected YYYY-MM-DD HH:MM:SS` برگردانده میشود.
**مثال ورودی معتبر:**
```json
{
"date": "2024-01-01",
"check_in": "2024-01-01 09:00:00",
"check_out": "2024-01-01 17:00:00",
"programmer_id": 1
}
```
**مثال ورودی نامعتبر:**
```json
{
"date": "01-01-2024",
"check_in": "09:00:00",
"check_out": "2024-01-01 17:00:00",
"programmer_id": 1
}
```
</details>
<details class="blue">
<summary>
متد `MarshalJSON` برای `Attendance`
</summary>
```go
func (a Attendance) MarshalJSON() ([]byte, error)
```
+ ساختار `Attendance` را به JSON تبدیل میکند.
+ فیلدهای `date`، `check_in` و `check_out` از نوع `time.Time` به رشته (`string`) تبدیل میشوند.
+ فرمتهای خروجی:
+ `date`: `YYYY-MM-DD` در `UTC`
+ `check_in`: `YYYY-MM-DD HH:MM:SS` در `UTC`
+ `check_out`: `YYYY-MM-DD HH:MM:SS` در `UTC`
+ در صورت خطا در تبدیل، خطا برگردانده میشود.
**مثال خروجی:**
```json
{
"id": 1,
"programmer_id": 1,
"date": "2024-01-01",
"check_in": "2024-01-01 09:00:00",
"check_out": "2024-01-01 17:00:00"
}
```
</details>
<details class="blue">
<summary>
متد `UnmarshalJSON` برای `AttendanceInput`
</summary>
```go
func (i *AttendanceInput) UnmarshalJSON(data []byte) error
```
+ دادههای JSON را به ساختار `AttendanceInput` تبدیل میکند.
+ فیلد `date` اجباری است و باید به فرمت `YYYY-MM-DD` باشد.
+ فیلدهای `check_in` و `check_out` اختیاری هستند و اگر وجود داشته باشند، باید به فرمت `YYYY-MM-DD HH:MM:SS` باشند.
+ در صورت خطا در تبدیل فرمتها یا عدم وجود فیلد `date`، خطا برگردانده میشود.
**خطاهای ممکن:**
+ اگر `date` وجود نداشته باشد، خطای `date field is necessary` برگردانده میشود.
+ اگر `date` فرمت نامعتبر داشته باشد، خطای `invalid date format, expected YYYY-MM-DD` برگردانده میشود.
+ اگر `check_in` یا `check_out` فرمت نامعتبر داشته باشند، خطای `invalid check_in/check_out format, expected YYYY-MM-DD HH:MM:SS` برگردانده میشود.
**مثال ورودی معتبر:**
```json
{
"date": "2024-01-01",
"check_in": "2024-01-01 09:00:00",
"check_out": "2024-01-01 17:00:00"
}
```
**مثال ورودی نامعتبر:**
```json
{
"date": "2024/01/01",
"check_in": "09:00:00"
}
```
</details>
**نکته:** برای یکتا بودن `ProgrammerID` و `Date` در `Attendance` میتوانید از ایندکس کمک بگیرید.
### هندلرها
شما باید هندلرهای *HTTP* اندپوینتهای زیر را با مشخصات دقیق آنها که در ادامه بیان خواهد شد پیادهسازی کنید:
```go
func RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("POST /attendance", CreateAttendanceHandler)
mux.HandleFunc("GET /attendance/{programmer_id}", GetAttendanceHandler)
mux.HandleFunc("PUT /attendance/{programmer_id}", UpdateAttendanceHandler)
mux.HandleFunc("DELETE /attendance/{programmer_id}", DeleteAttendanceHandler)
mux.HandleFunc("DELETE /attendance/{programmer_id}/{date}", DeleteOneDayAttendanceHandler)
mux.HandleFunc("GET /attendance", GetAllAttendanceHandler)
mux.HandleFunc("GET /girinof/{programmer_id}/{date}", GetGirinofReportHandler)
mux.HandleFunc("GET /report/monthly/{programmer_id}/{start_date}/{end_date}", GetMonthlyReportHandler)
mux.HandleFunc("GET /salary/{programmer_id}", GetSalaryHandler)
mux.HandleFunc("POST /programmer", CreateProgrammerHandler)
}
```
<details class="blue">
<summary>
هندلر `CreateAttendanceHandler`
</summary>
توضیح:
این هندلر برای ثبت حضور و غیاب برنامهنویسان است. ابتدا دادههای ورودی را از ریکوئست دریافت کرده و اعتبار آنها را بررسی میکند (مانند فرمت درست، وجود فیلد تاریخ، و ترتیب ورود و خروج). در صورت معتبر بودن، اطلاعات حضور را در سیستم ذخیره کرده و پاسخ مناسب ارسال میکند.
+ متد: `POST`
+ اندپوینت یا مسیر: `/attendance`
+ ورودی: بدنه *JSON* شامل:
```json
{
"programmer_id": uint,
"date": "YYYY-MM-DD",
"check_in": "YYYY-MM-DD HH:MM:SS",
"check_out": "YYYY-MM-DD HH:MM:SS"
}
```
اعتبارسنجی:
+ در صورت دیکد نشدن *JSON* باید `Invalid input format` را با وضعیت 400 برگردانید.
+ در صورت عدم وجود فیلد تاریخ یا `date` باید `Missing required field: date` را با وضعیت 400 برگردانید.
+ در صورتی که ساعت خروج قبل از ساعت ورود بود، باید پیام `CheckOut cannot be before CheckIn` را با وضعیت 400 برگردانید.
+ در صورتی که `programmer_id` ارسالشده در پایگاهداده موجود نبود باید پیام `Programmer not found! Please hire the programmer first`. را با وضعیت 404 برگردانید.
پاسخ موفقیتآمیز:
+ وضعیت: 201 (ایجاد شده)
+ `Content-Type`: `application/json`
+ `Body`: شی `attendance` ایجاد شده
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to create attendance:` + پیام خطا (اگر رکوردی با همان `programmer_id` و `date` وجود داشته باشد، باید `Duplicate attendance record` را برگرداند که شما این مورد را در لایهی ریپوزیتوری هندل میکنید)
</details>
<details class="blue"> <summary> هندلر `GetAttendanceHandler` </summary>
توضیح:
این هندلر برای دریافت سوابق حضور و غیاب یک برنامهنویس خاص است. ابتدا شناسه برنامهنویس را از مسیر URL استخراج کرده و اعتبارسنجی میکند. سپس، اگر دادهای موجود باشد، سوابق حضور را در قالب JSON برمیگرداند، در غیر این صورت پیام خطای مناسب ارسال میشود.
- متد: `GET`
- اندپوینت یا مسیر: `/attendance/{programmer_id}`
- ورودی: `programmer_id` به عنوان پارامتر مسیر
اعتبارسنجی:
- اگر `programmer_id` نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید `Invalid Programmer ID` را با وضعیت 400 برگردانید.
- اگر هیچ رکورد حضور و غیابی برای `programmer_id` دادهشده وجود نداشته باشد، باید `No attendance records found for the given Programmer ID` را با وضعیت 404 برگردانید.
پاسخ موفقیتآمیز:
- وضعیت: 200 (موفق)
- `Content-Type`: `application/json`
- `Body`: لیستی از رکوردهای حضور و غیاب مربوط به `programmer_id`
پاسخ خطا:
- وضعیت: 500 با پیام `Error retrieving attendance records` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `UpdateAttendanceHandler`
</summary>
توضیح:
این هندلر برای بهروزرسانی اطلاعات حضور و غیاب یک برنامهنویس است. ابتدا شناسه برنامهنویس و تاریخ موردنظر را بررسی کرده و در صورت وجود، اطلاعات جدید (مانند ورود و خروج) را ثبت میکند. اگر دادهای نامعتبر باشد یا رکورد حضور قبلاً ثبت نشده باشد، پیام خطای مناسب ارسال میشود.
+ متد: `PUT`
+ اندپوینت یا مسیر: `/attendance/{programmer_id}`
+ ورودی: بدنه _JSON_ دو فیلد ورود و خروج به صورت **اختیاری**:
```json
{
"date": "YYYY-MM-DD",
"check_in": "YYYY-MM-DD HH:MM:SS",
"check_out": "YYYY-MM-DD HH:MM:SS"
}
```
اعتبارسنجی:
+ اگر `programmer_id` نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید `Invalid Programmer ID"` را با وضعیت 400 برگردانید.
+ اگر `programmer_id` در دیتابیس وجود نداشته باشد، باید `Programmer not found` را با وضعیت 404 برگردانید.
+ اگر فیلد date یا programmer_id ارسال نشوند باید پیام `Programmer ID is required` و `Date is required` با وضعیت 400 برگردانید.
+ اگر `date` ارسالشده برای `programmer_id` موجود نباشد، باید `Programmer with the given date not recorded. Please use CreateAttendanceHandler to create it!` را با وضعیت 400 برگردانید.
+ اگر تاریخ خروج قبل از تاریخ ورود باشد، باید `CheckIn cannot be after CheckOut` را با وضعیت 400 برگردانید.
پاسخ موفقیتآمیز:
+ وضعیت: 200 (موفق)
+ `Content-Type`: `application/json`
+ `Body`: گزارش `GirinofReport` به شکل زیر:
```json
{
"programmer_id": "1",
"total_delays": 2,
"total_early_departures": 3
}
```
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to update attendance` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `DeleteAttendanceHandler`
</summary>
توضیح:
این هندلر برای حذف سوابق حضور و غیاب یک برنامهنویس است. ابتدا شناسه برنامهنویس را بررسی کرده و در صورت معتبربودن، رکورد یا رکوردهای حضور مربوطه را حذف میکند. در صورت نامعتبربودن شناسه یا بروز خطا، پیام خطای مناسب ارسال میشود.
+ متد: `DELETE`
+ اندپوینت یا مسیر: `/attendance/{programmer_id}`
+ ورودی: `programmer_id` به عنوان پارامتر مسیر
اعتبارسنجی:
+ اگر `programmer_id` نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید `Invalid programmer ID` را با وضعیت 400 برگردانید.
+ اگر برنامهنویسی با آیدی متناظر با `programmer_id` موجود نباشد، باید `Programmer not found` را با وضعیت 404 برگردانید.
+ اگر `date` در قالب `YYYY-MM-DD` نباشد، باید `Invalid date format` را با وضعیت **400** برگرداند. (این مورد را در بخش توابع `Marshal/Unmarshal` جیسون هندل میکنید)
پاسخ موفقیتآمیز:
+ وضعیت: 202 (پذیرفتهشده)
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to delete attendance` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `DeleteOneDayAttendanceHandler`
</summary>
توضیح:
این هندلر برای حذف سوابق حضور و غیاب یک برنامهنویس در یک تاریخ مشخص است. ابتدا شناسه برنامهنویس و تاریخ را از مسیر URL دریافت کرده و بررسی میکند. در صورت معتبربودن و وجود رکورد، آن را حذف میکند؛ در غیر این صورت، پیام خطای مناسب ارسال میشود.
+ متد: `DELETE`
+ اندپوینت یا مسیر: `/attendance/{programmer_id}/{date}`
+ ورودی:
+ `programmer_id` به عنوان پارامتر مسیر
+ `date` به عنوان پارامتر مسیر (فرمت: `YYYY-MM-DD`)
اعتبارسنجی:
+ اگر `programmer_id` نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید `Invalid programmer ID` را با وضعیت 400 برگردانید.
+ اگر `date` فرمت نامعتبر داشته باشد، باید `Invalid Date` را با وضعیت 400 برگردانید.
+ اگر `programmer_id` یا `date` مورد نظر وجود نداشت، باید `No attendance record found for the given date` با وضعیت 404 را بازگردانید.
پاسخ موفقیتآمیز:
+ وضعیت: 202 (پذیرفتهشده)
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to delete attendance` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `GetAllAttendanceHandler`
</summary>
توضیح:
این هندلر برای دریافت تمامی سوابق حضور و غیاب است. ابتدا تمام دادههای حضور را از پایگاهداده واکشی کرده و در قالب JSON بازمیگرداند. در صورت بروز خطا در دریافت اطلاعات، پیام خطای مناسب ارسال میشود.
+ متد: `GET`
+ اندپوینت یا مسیر: `/attendance`
+ ورودی: ندارد
اعتبارسنجی:
+ نیاز به اعتبارسنجی خاصی ندارد (همه رکوردها را برمیگرداند)
پاسخ موفقیتآمیز:
+ وضعیت: 200 (موفق)
+ `Content-Type`: `application/json`
+ `Body`: لیست کامل تمامی رکوردهای حضور و غیاب
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to fetch attendance records` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `GetGirinofReportHandler`
</summary>
توضیح:
این هندلر برای دریافت گزارش حضور و غیاب یک برنامهنویس در یک تاریخ مشخص است. ابتدا شناسه برنامهنویس و تاریخ را بررسی کرده و در صورت وجود اطلاعات، گزارش را تولید و در قالب *JSON* بازمیگرداند. در صورت عدم وجود داده یا خطای پردازش، پیام خطای مناسب ارسال میشود.
+ متد: `GET`
+ اندپوینت یا مسیر: `/girinof/{programmer_id}/{date}`
+ ورودی:
+ `programmer_id` به عنوان پارامتر مسیر
+ `date` به عنوان پارامتر مسیر (فرمت: `YYYY-MM-DD`)
اعتبارسنجی:
+ اگر `programmer_id` ارسال نشده باشد، باید `Programmer ID is required` را با وضعیت **400** برگرداند.
+ اگر `date` ارسال نشده باشد، باید `Date is required` را با وضعیت **400** برگرداند.
+ اگر `date` فرمت نامعتبر داشته باشد، باید `Invalid Date` را با وضعیت **400** برگرداند.
+ اگر `programmer_id` عدد نباشد یا کمتر از ۱ باشد، باید `Invalid Programmer ID` را با وضعیت **400** برگرداند.
+ اگر برنامهنویس با `programmer_id` دادهشده وجود نداشته باشد، باید `Programmer not found` را با وضعیت **404** برگرداند.
+ اگر رکورد حضور برای `programmer_id` و `date` دادهشده وجود نداشته باشد، باید `Programmer with the given date not recorded. Please use CreateAttendanceHandler to create it!` را با وضعیت **400** برگرداند.
پاسخ موفقیتآمیز:
+ وضعیت: 200 (موفق)
+ `Content-Type`: `application/json`
+ `Body`: گزارش `GirinofReport`
+ محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to generate report` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `GetMonthlyReportHandler`
</summary>
توضیح:
این هندلر برای دریافت گزارش حضور و غیاب یک برنامهنویس در یک بازه زمانی مشخص است. ابتدا شناسه برنامهنویس، تاریخ شروع و پایان را بررسی کرده و در صورت معتبر بودن، گزارش را تولید و در قالب *JSON* بازمیگرداند. در صورت نامعتبر بودن ورودیها یا بروز خطا، پیام خطای مناسب ارسال میشود.
+ متد: `GET`
+ اندپوینت یا مسیر: `/report/monthly/{programmer_id}/{start_date}/{end_date}`
+ ورودی:
+ `programmer_id` به عنوان پارامتر مسیر
+ `start_date` و `end_date` به عنوان پارامتر مسیر (فرمت: `YYYY-MM-DD`)
اعتبارسنجی:
+ اگر `programmer_id` ارسال نشده باشد، باید `Programmer ID is required` را با وضعیت **400** برگرداند.
+ اگر `start_date` یا `end_date` ارسال نشده باشد، باید `Start date is required` یا `End date is required` را با وضعیت **400** برگرداند.
+ اگر `programmer_id` عدد نباشد یا کمتر از ۱ باشد، باید `Invalid programmer ID` را با وضعیت **400** برگرداند.
+ اگر `start_date` یا `end_date` فرمت نامعتبر داشته باشند، باید `Invalid start date format` یا `Invalid end date format` را با وضعیت **400** برگرداند.
+ اگر `end_date` قبل از `start_date` باشد، باید `End date cannot be before start date` را با وضعیت **400** برگرداند.
+ اگر برنامهنویس با `programmer_id` دادهشده وجود نداشته باشد، باید `Programmer not found` را با وضعیت **404** برگرداند.
پاسخ موفقیتآمیز:
+ وضعیت: 200 (موفق)
+ `Content-Type`: `application/json`
+ `Body`: گزارش `MonthlySummary` به شکل زیر:
```json
{
"programmer_id": "1",
"total_days_present": 4,
"total_overtime_minutes": 3,
"total_delay_minutes": 2,
"total_early_departure_minutes": 1
}
```
+ محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to generate monthly report` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `GetSalaryHandler`
</summary>
توضیح:
این هندلر برای محاسبه حقوق یک برنامهنویس در ۳۰ روز گذشته است. ابتدا شناسه برنامهنویس را بررسی کرده و در صورت معتبربودن، حقوق او را بر اساس سوابق حضور و غیاب محاسبه و در قالب *JSON* بازمیگرداند. در صورت عدم وجود اطلاعات یا بروز خطا، پیام خطای مناسب ارسال میشود.
+ متد: `GET`
+ اندپوینت یا مسیر: `/salary/{programmer_id}`
+ ورودی: `programmer_id` به عنوان پارامتر مسیر
اعتبارسنجی:
+ اگر `programmer_id` ارسال نشده باشد، باید `Programmer ID is required` را با وضعیت **400** برگرداند.
+ اگر `programmer_id` عدد نباشد یا کمتر از ۱ باشد، باید `Invalid programmer ID` را با وضعیت **400** برگرداند.
+ اگر برنامهنویس با `programmer_id` دادهشده وجود نداشته باشد، باید `Programmer not found` را با وضعیت **404** برگرداند.
پاسخ موفقیتآمیز:
+ وضعیت: 200 (موفق)
+ `Content-Type`: `application/json`
+ `Body`: گزارش `SalaryReport` به شکل زیر:
```json
{
"name": "Younes Sinwar",
"total_days_present": 30,
"total_overtime_minutes": 1,
"total_delay_minutes": 2,
"total_early_departure_minutes": 3,
"total_salary": 3000.0
}
```
+ محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
محاسبه حقوق: حقوق بر اساس فرمول زیر محاسبه میشود:
حقوق پایه = تعداد روزهای حضور × DailyRate (۱۰۰)
اضافهکاری = مجموع دقیقههای اضافهکاری × OvertimeRate (۲۰)
جریمه تاخیر = مجموع دقیقههای تاخیر × DelayPenalty (۱۰)
حقوق نهایی = حقوق پایه + اضافهکاری - جریمه تاخیر
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to calculate salary` + پیام خطا
</details>
<details class="blue">
<summary>
هندلر `CreateProgrammerHandler`
</summary>
توضیح:
این هندلر برای ایجاد یک برنامهنویس جدید در سیستم است. ابتدا دادههای ورودی را بررسی کرده و در صورت معتبربودن، برنامهنویس را در پایگاهداده ثبت میکند. در صورت بروز خطا یا نامعتبربودن اطلاعات، پیام خطای مناسب ارسال میشود.
+ متد: `POST`
+ اندپوینت یا مسیر: `/programmer`
+ ورودی: بدنه _JSON_ شامل:
```json
{
"name": "string"
}
```
اعتبارسنجی:
+ اگر `name` ارسال نشده باشد، باید `Invalid input format` را با وضعیت 400 برگردانید.
+ + اگر فیلد `name` خالی باشد، باید `Name is required` را با وضعیت 400 برگردانید.
پاسخ موفقیتآمیز:
+ وضعیت: 201 (ایجاد شده)
+ `Content-Type`: `application/json`
+ `Body`: شی `programmer` ایجادشده
پاسخ خطا:
+ وضعیت: 500 با پیام `Failed to create programmer` + پیام خطا
</details>
+ در تمام هندلرها اگر خطای داخلی سرور رخ دهد، باید وضعیت **500** برگردانده شود.
### توابع Repository
متدهای *Repository* زیر را پیادهسازی کنید:
```go
func (repo *Repository) CreateAttendance(attendance *Attendance) error
func (repo *Repository) GetAttendancesByProgrammer(pid uint) ([]Attendance, error)
func (repo *Repository) GetAllAttendance() ([]Attendance, error)
func (repo *Repository) UpdateAttendance(attendance *Attendance) error
func (repo *Repository) DeleteAttendance(pid uint) error
func (repo *Repository) DeleteOneDayAttendance(pid uint, date time.Time) error
func (repo *Repository) GetGirinofReport(pid uint, date time.Time) (*GirinofReport, error)
func (repo *Repository) GetMonthlyReport(pid uint, startDate, endDate time.Time) (*MonthlySummary, error)
func (repo *Repository) CalculateSalary(pid uint, startDate, endDate time.Time) (*SalaryReport, error)
func (repo *Repository) CreateProgrammer(name string) (*Programmer, error)
func (repo *Repository) GetProgrammerByID(id uint) (*Programmer, error)
func (repo *Repository) IsExistDateAndProgrammerID(id uint, date time.Time) (*Attendance, bool)
```
<details class="blue">
<summary>
متد `CreateAttendance`
</summary>
```go
func (repo *Repository) CreateAttendance(attendance *Attendance) error
```
+ رکورد `Attendance` جدیدی با تمام فیلدهای الزامی ایجاد میکند
+ در صورت وجود `attendance` تکراری (با همان `ProgrammerID` و `Date`) خطا برمیگرداند
+ آیدی تولید شده برای رکورد جدید باید غیرصفر باشد
+ در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند
</details>
<details class="blue">
<summary>
متد `GetAttendancesByProgrammer`
</summary>
```go
func (repo *Repository) GetAttendancesByProgrammer(pid uint) ([]Attendance, error)
```
+ لیست تمام رکوردهای حضور و غیاب یک برنامهنویس را برمیگرداند
+ نتایج باید بر اساس تاریخ (`date`) به صورت نزولی مرتب شوند
+ حداکثر ۱۲ رکورد بازگردانده شود
+ در صورت عدم وجود رکورد، لیست خالی برمیگرداند
+ در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند
</details>
<details class="blue">
<summary>
متد `GetAllAttendance`
</summary>
```go
func (repo *Repository) GetAllAttendance() ([]Attendance, error)
```
+ لیست تمامی رکوردهای حضور و غیاب موجود در پایگاهداده را برمیگرداند.
+ در صورت عدم وجود رکورد، لیست خالی برمیگرداند.
+ در صورت خطا، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `UpdateAttendance`
</summary>
```go
func (repo *Repository) UpdateAttendance(attendance *Attendance) error
```
+ رکورد موجود را با مقادیر جدید بهروزرسانی میکند.
+ فیلدهای قابل بهروزرسانی: `date`، `check_in` و `check_out`
+ در صورت عدم وجود رکورد مرتبط، خطا برمیگرداند.
+ در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `DeleteAttendance`
</summary>
```go
func (repo *Repository) DeleteAttendance(pid uint) error
```
+ تمام رکوردهای حضور و غیاب مربوط به یک برنامهنویس را حذف میکند.
+ پس از حذف، هیچ رکوردی با ProgrammerID مربوطه نباید وجود داشته باشد.
+ در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `DeleteOneDayAttendance`
</summary>
```go
func (repo *Repository) DeleteOneDayAttendance(pid uint, date time.Time) error
```
+ رکورد حضور و غیاب مربوط به یک روز خاص را حذف میکند.
+ تاریخ باید دقیقاً مطابقت داشته باشد (برای انجام این کار تمامی رکوردهای تاریخ آن روز را از ساعت `00:00:00` تا ساعت `24:00:00` حذف کنید. میتوانید از `date.Truncate` استفاده کنید)
+ در صورت عدم وجود رکورد، خطا برمیگرداند.
+ در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `GetGirinofReport`
</summary>
```go
func (repo *Repository) GetGirinofReport(pid uint, date time.Time) (*GirinofReport, error)
```
+ گزارش روزانه شامل مجموع تاخیرها (`total_delay_minutes`) و خروجهای زودهنگام (`total_early_departure_minutes`) را همراه با `programmer_id` ایجاد میکند.
+ محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
+ در صورت عدم وجود رکورد در تاریخ مشخص، خطا برمیگرداند.
+ در صورت خطا در محاسبات یا عملیات پایگاهداده، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `GetMonthlyReport`
</summary>
```go
func (repo *Repository) GetMonthlyReport(id string, startDate, endDate time.Time) (*MonthlySummary, error)
```
+ گزارش ماهانه شامل آمار حضور، اضافهکاری و تاخیرها را در بازه زمانی داده شده ایجاد میکند
+ محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
+ این گزارش شامل آیدی برنامهنویس (`programmer_id`) تعداد روزهای حضور (`total_days_present`)، مجموع دقیقههای اضافهکاری (`total_overtime_minutes`)، مجموع دقیقههای تاخیر (`total_delay_minutes`) و مجموع دقیقههای خروج زودهنگام (`total_early_departure_minutes`) است.
+ در صورت خطا در محاسبات یا عملیات پایگاهداده، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `CalculateSalary`
</summary>
```go
func (repo *Repository) CalculateSalary(id string, startDate, endDate time.Time) (*SalaryReport, error)
```
+ حقوق (`SalaryReport`) یک برنامهنویس خاص را در سی روز گذشته محاسبه میکند.
+ محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
+ حقوق خالص را بر اساس فرمول زیر محاسبه میکند:
+ حقوق پایه = تعداد روزهای حضور × نرخ روزانه (`DailyRate`)
+ اضافهکاری = مجموع دقیقههای اضافهکاری × نرخ اضافهکاری (`OvertimeRate`)
+ جریمه تاخیر = مجموع دقیقههای تاخیر × نرخ جریمه (`DelayPenalty`)
+ حقوق نهایی = حقوق پایه + اضافهکاری - جریمه تاخیر
+ این گزارش شامل نام برنامهنویس (`name`)، تعداد روزهای حضور (`total_days_present`)، مجموع دقیقههای اضافهکاری (`total_overtime_minutes`)، مجموع دقیقههای تاخیر (`total_delay_minutes`)، مجموع دقیقههای خروج زودهنگام (`total_early_departure_minutes`) و مجموع حقوق (`total_salary`) است.
+ در صورت خطا در محاسبات یا عملیات پایگاهداده، خطا را برمیگرداند
</details>
<details class="blue">
<summary>
متد `CreateProgrammer`
</summary>
```go
func (repo *Repository) CreateProgrammer(name string) (*Programmer, error)
```
+ یک برنامهنویس جدید ایجاد میکند.
+ آیدی تولید شده باید غیرصفر باشد.
+ در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `GetProgrammerByID`
</summary>
```go
func (repo *Repository) GetProgrammerByID(id uint) (*Programmer, error)
```
+ اطلاعات کامل برنامهنویس را بر اساس ID برمیگرداند.
+ در صورت عدم وجود برنامهنویس با ID داده شده، خطا برمیگرداند.
+ در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
</details>
<details class="blue">
<summary>
متد `IsExistDateAndProgrammerID`
</summary>
```go
func (repo *Repository) IsExistDateAndProgrammerID(id uint, date time.Time) (*Attendance, bool)
```
+ بررسی میکند که آیا رکورد حضور و غیاب برای یک برنامهنویس خاص در تاریخ مشخص وجود دارد یا خیر.
+ تاریخ باید دقیقاً مطابقت داشته باشد (برای انجام این کار تمامی رکوردهای تاریخ آن در آن روز یعنی از ساعت `00:00:00` تا ساعت `24:00:00` را بررسی کنید)
+ در صورت وجود، رکورد کامل را همراه با `true` برمیگرداند.
+ در صورت عدم وجود، `nil` و `false` برمیگرداند.
+ تاریخ باید دقیقاً مطابقت داشته باشد (با در نظر گرفتن ساعت ۰۰:۰۰:۰۰)
</details>
**نکات**
+ شما باید در این سوال از *GORM* و ویژگیهای آن استفاده کنید و به جز **پکیجهای موجود در پروژه اولیه** اجازه استفاده از **پکیج دیگری** را **ندارید**.
+ فایل `db.go` برای اتصال *GORM* به پایگاهداده با استفاده از الگوی طراحی *singleton* است. شما نیازی به تغییر آن ندارید به جز تغییر ثابتها برای تست روی محیط لوکال خود.
+ فایلها و کدهای پروژهاولیه را نباید تغییر بدهید و صرفا باید مکانهایی که با کامنت `// TODO Implement` برای شما مشخص شده را پیادهسازی کنید.
+ نیازی به ارسال فایلهای `go.mod` و `go.sum` ندارید!
**چه چیزی را آپلود کنید**
پس از پیادهسازی ویژگیهای خواستهشده، **فقط** دایرکتوری `app` که شامل سه فایل `handlers.go` و `models.go` و `repository.go` است را فشرده کرده و صورت زیپ ارسال کنید.
فایلهایی که نیاز به ویرایش دارند:
+ `app/models.go`
+ `app/handlers.go`
+ `app/repository.go`
گیرینُف
تیم همکارانسیستم تصمیم گرفته سیستم ردیابی ساعات ورود و خروج خود را با قابلیت شخصیسازی برای تیم HR طراحی کند. همکاران که بهشدت شرکت منظمی است و روی نظم کارمندان خود نیز حساس است از شما میخواهد برای ورود و استخدام به این شرکت و کمک به تیم منابع انسانی، بکاند سیستم ترکینگ ورود و خروج برنامهنویسان برای تهیه گزارشها و محاسبه حقوقشان را پیادهسازی کنید.
جزئیات پروژه🔗
پروژه اولیه را از این لینک دانلود کنید. ساختار پروژه به شکل زیر است:
در این پروژه، شما باید قسمتهای مشخص شده با // TODO
را پیادهسازی کنید. در نهایت شما از طریق اجرای فایل باینری main.go
میتوانید پروژه خود را اجرا کنید.
مدلهای پایگاهداده🔗
ساختار جداول پروژه به شرح زیر باشد:
1- جدول Programmer
Column Name |
Type |
Notes |
ID |
uint |
PRIMARY KEY AUTO_INCREMENT |
Name |
string |
Programmer's name |
CreatedAt |
time.Time |
Creation timestamp |
UpdatedAt |
time.Time |
Last update timestamp |
2- جدول Attendance
Column Name |
Type |
Notes |
ID |
uint |
PRIMARY KEY AUTO_INCREMENT |
ProgrammerID |
uint |
Foreign key to Programmer table |
Date |
time.Time |
Attendance date |
CheckIn |
time.Time |
Check-in time |
CheckOut |
time.Time |
Check-out time |
CreatedAt |
time.Time |
Creation timestamp |
UpdatedAt |
time.Time |
Last update timestamp |
روابط🔗
روابط بین مدلها باید به شرح زیر پیادهسازی شوند:
- هر برنامهنویس میتواند دارای چندین رکورد ورود و خروج باشد.
- هر رکورد ورود و خروج متعلق به یک برنامهنویس است.
- هر برنامهنویس در هر روز فقط باید یک رکورد ورود و خروج در سیستم ثبت نماید.
توابع Marshal/Unmarshal
جیسون🔗
این توابع باید تبدیل صحیح بین JSON و ساختارهای Go برای مدل Attendance
و AttendanceInput
را مدیریت کنند:
متد UnmarshalJSON
برای Attendance
دادههای JSON را به ساختار Attendance
تبدیل میکند.
فیلدهای date
، check_in
و check_out
از نوع رشته (string
) دریافت میشوند و به نوع time.Time
تبدیل میشوند.
فرمتهای مورد انتظار:
در صورت خطا در تبدیل فرمتها، خطا برگردانده میشود.
خطاهای ممکن:
اگر date
فرمت نامعتبر داشته باشد، خطای parsing time
با پیام expected YYYY-MM-DD
برگردانده میشود.
اگر check_in
یا check_out
فرمت نامعتبر داشته باشند، خطای parsing time
با پیام expected YYYY-MM-DD HH:MM:SS
برگردانده میشود.
مثال ورودی معتبر:
مثال ورودی نامعتبر:
متد MarshalJSON
برای Attendance
ساختار Attendance
را به JSON تبدیل میکند.
فیلدهای date
، check_in
و check_out
از نوع time.Time
به رشته (string
) تبدیل میشوند.
فرمتهای خروجی:
در صورت خطا در تبدیل، خطا برگردانده میشود.
مثال خروجی:
متد UnmarshalJSON
برای AttendanceInput
دادههای JSON را به ساختار AttendanceInput
تبدیل میکند.
فیلد date
اجباری است و باید به فرمت YYYY-MM-DD
باشد.
فیلدهای check_in
و check_out
اختیاری هستند و اگر وجود داشته باشند، باید به فرمت YYYY-MM-DD HH:MM:SS
باشند.
در صورت خطا در تبدیل فرمتها یا عدم وجود فیلد date
، خطا برگردانده میشود.
خطاهای ممکن:
اگر date
وجود نداشته باشد، خطای date field is necessary
برگردانده میشود.
اگر date
فرمت نامعتبر داشته باشد، خطای invalid date format, expected YYYY-MM-DD
برگردانده میشود.
اگر check_in
یا check_out
فرمت نامعتبر داشته باشند، خطای invalid check_in/check_out format, expected YYYY-MM-DD HH:MM:SS
برگردانده میشود.
مثال ورودی معتبر:
مثال ورودی نامعتبر:
نکته: برای یکتا بودن ProgrammerID
و Date
در Attendance
میتوانید از ایندکس کمک بگیرید.
هندلرها🔗
شما باید هندلرهای HTTP اندپوینتهای زیر را با مشخصات دقیق آنها که در ادامه بیان خواهد شد پیادهسازی کنید:
هندلر CreateAttendanceHandler
توضیح:
این هندلر برای ثبت حضور و غیاب برنامهنویسان است. ابتدا دادههای ورودی را از ریکوئست دریافت کرده و اعتبار آنها را بررسی میکند (مانند فرمت درست، وجود فیلد تاریخ، و ترتیب ورود و خروج). در صورت معتبر بودن، اطلاعات حضور را در سیستم ذخیره کرده و پاسخ مناسب ارسال میکند.
- متد:
POST
- اندپوینت یا مسیر:
/attendance
- ورودی: بدنه JSON شامل:
اعتبارسنجی:
- در صورت دیکد نشدن JSON باید
Invalid input format
را با وضعیت 400 برگردانید.
- در صورت عدم وجود فیلد تاریخ یا
date
باید Missing required field: date
را با وضعیت 400 برگردانید.
- در صورتی که ساعت خروج قبل از ساعت ورود بود، باید پیام
CheckOut cannot be before CheckIn
را با وضعیت 400 برگردانید.
- در صورتی که
programmer_id
ارسالشده در پایگاهداده موجود نبود باید پیام Programmer not found! Please hire the programmer first
. را با وضعیت 404 برگردانید.
پاسخ موفقیتآمیز:
- وضعیت: 201 (ایجاد شده)
Content-Type
: application/json
Body
: شی attendance
ایجاد شده
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to create attendance:
+ پیام خطا (اگر رکوردی با همان programmer_id
و date
وجود داشته باشد، باید Duplicate attendance record
را برگرداند که شما این مورد را در لایهی ریپوزیتوری هندل میکنید)
هندلر GetAttendanceHandler
توضیح:
این هندلر برای دریافت سوابق حضور و غیاب یک برنامهنویس خاص است. ابتدا شناسه برنامهنویس را از مسیر URL استخراج کرده و اعتبارسنجی میکند. سپس، اگر دادهای موجود باشد، سوابق حضور را در قالب JSON برمیگرداند، در غیر این صورت پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
اگر programmer_id
نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid Programmer ID
را با وضعیت 400 برگردانید.
اگر هیچ رکورد حضور و غیابی برای programmer_id
دادهشده وجود نداشته باشد، باید No attendance records found for the given Programmer ID
را با وضعیت 404 برگردانید.
پاسخ موفقیتآمیز:
پاسخ خطا:
- وضعیت: 500 با پیام
Error retrieving attendance records
+ پیام خطا
هندلر UpdateAttendanceHandler
توضیح:
این هندلر برای بهروزرسانی اطلاعات حضور و غیاب یک برنامهنویس است. ابتدا شناسه برنامهنویس و تاریخ موردنظر را بررسی کرده و در صورت وجود، اطلاعات جدید (مانند ورود و خروج) را ثبت میکند. اگر دادهای نامعتبر باشد یا رکورد حضور قبلاً ثبت نشده باشد، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
اگر programmer_id
نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid Programmer ID"
را با وضعیت 400 برگردانید.
اگر programmer_id
در دیتابیس وجود نداشته باشد، باید Programmer not found
را با وضعیت 404 برگردانید.
اگر فیلد date یا programmer_id ارسال نشوند باید پیام Programmer ID is required
و Date is required
با وضعیت 400 برگردانید.
اگر date
ارسالشده برای programmer_id
موجود نباشد، باید Programmer with the given date not recorded. Please use CreateAttendanceHandler to create it!
را با وضعیت 400 برگردانید.
اگر تاریخ خروج قبل از تاریخ ورود باشد، باید CheckIn cannot be after CheckOut
را با وضعیت 400 برگردانید.
پاسخ موفقیتآمیز:
- وضعیت: 200 (موفق)
Content-Type
: application/json
Body
: گزارش GirinofReport
به شکل زیر:
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to update attendance
+ پیام خطا
هندلر DeleteAttendanceHandler
توضیح:
این هندلر برای حذف سوابق حضور و غیاب یک برنامهنویس است. ابتدا شناسه برنامهنویس را بررسی کرده و در صورت معتبربودن، رکورد یا رکوردهای حضور مربوطه را حذف میکند. در صورت نامعتبربودن شناسه یا بروز خطا، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
اگر programmer_id
نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid programmer ID
را با وضعیت 400 برگردانید.
اگر برنامهنویسی با آیدی متناظر با programmer_id
موجود نباشد، باید Programmer not found
را با وضعیت 404 برگردانید.
اگر date
در قالب YYYY-MM-DD
نباشد، باید Invalid date format
را با وضعیت 400 برگرداند. (این مورد را در بخش توابع Marshal/Unmarshal
جیسون هندل میکنید)
پاسخ موفقیتآمیز:
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to delete attendance
+ پیام خطا
هندلر DeleteOneDayAttendanceHandler
توضیح:
این هندلر برای حذف سوابق حضور و غیاب یک برنامهنویس در یک تاریخ مشخص است. ابتدا شناسه برنامهنویس و تاریخ را از مسیر URL دریافت کرده و بررسی میکند. در صورت معتبربودن و وجود رکورد، آن را حذف میکند؛ در غیر این صورت، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
اگر programmer_id
نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid programmer ID
را با وضعیت 400 برگردانید.
اگر date
فرمت نامعتبر داشته باشد، باید Invalid Date
را با وضعیت 400 برگردانید.
اگر programmer_id
یا date
مورد نظر وجود نداشت، باید No attendance record found for the given date
با وضعیت 404 را بازگردانید.
پاسخ موفقیتآمیز:
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to delete attendance
+ پیام خطا
هندلر GetAllAttendanceHandler
توضیح:
این هندلر برای دریافت تمامی سوابق حضور و غیاب است. ابتدا تمام دادههای حضور را از پایگاهداده واکشی کرده و در قالب JSON بازمیگرداند. در صورت بروز خطا در دریافت اطلاعات، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
- نیاز به اعتبارسنجی خاصی ندارد (همه رکوردها را برمیگرداند)
پاسخ موفقیتآمیز:
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to fetch attendance records
+ پیام خطا
هندلر GetGirinofReportHandler
توضیح:
این هندلر برای دریافت گزارش حضور و غیاب یک برنامهنویس در یک تاریخ مشخص است. ابتدا شناسه برنامهنویس و تاریخ را بررسی کرده و در صورت وجود اطلاعات، گزارش را تولید و در قالب JSON بازمیگرداند. در صورت عدم وجود داده یا خطای پردازش، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
- اگر
programmer_id
ارسال نشده باشد، باید Programmer ID is required
را با وضعیت 400 برگرداند.
- اگر
date
ارسال نشده باشد، باید Date is required
را با وضعیت 400 برگرداند.
- اگر
date
فرمت نامعتبر داشته باشد، باید Invalid Date
را با وضعیت 400 برگرداند.
- اگر
programmer_id
عدد نباشد یا کمتر از ۱ باشد، باید Invalid Programmer ID
را با وضعیت 400 برگرداند.
- اگر برنامهنویس با
programmer_id
دادهشده وجود نداشته باشد، باید Programmer not found
را با وضعیت 404 برگرداند.
- اگر رکورد حضور برای
programmer_id
و date
دادهشده وجود نداشته باشد، باید Programmer with the given date not recorded. Please use CreateAttendanceHandler to create it!
را با وضعیت 400 برگرداند.
پاسخ موفقیتآمیز:
وضعیت: 200 (موفق)
Content-Type
: application/json
Body
: گزارش GirinofReport
محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to generate report
+ پیام خطا
هندلر GetMonthlyReportHandler
توضیح:
این هندلر برای دریافت گزارش حضور و غیاب یک برنامهنویس در یک بازه زمانی مشخص است. ابتدا شناسه برنامهنویس، تاریخ شروع و پایان را بررسی کرده و در صورت معتبر بودن، گزارش را تولید و در قالب JSON بازمیگرداند. در صورت نامعتبر بودن ورودیها یا بروز خطا، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
- اگر
programmer_id
ارسال نشده باشد، باید Programmer ID is required
را با وضعیت 400 برگرداند.
- اگر
start_date
یا end_date
ارسال نشده باشد، باید Start date is required
یا End date is required
را با وضعیت 400 برگرداند.
- اگر
programmer_id
عدد نباشد یا کمتر از ۱ باشد، باید Invalid programmer ID
را با وضعیت 400 برگرداند.
- اگر
start_date
یا end_date
فرمت نامعتبر داشته باشند، باید Invalid start date format
یا Invalid end date format
را با وضعیت 400 برگرداند.
- اگر
end_date
قبل از start_date
باشد، باید End date cannot be before start date
را با وضعیت 400 برگرداند.
- اگر برنامهنویس با
programmer_id
دادهشده وجود نداشته باشد، باید Programmer not found
را با وضعیت 404 برگرداند.
پاسخ موفقیتآمیز:
- محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to generate monthly report
+ پیام خطا
هندلر GetSalaryHandler
توضیح:
این هندلر برای محاسبه حقوق یک برنامهنویس در ۳۰ روز گذشته است. ابتدا شناسه برنامهنویس را بررسی کرده و در صورت معتبربودن، حقوق او را بر اساس سوابق حضور و غیاب محاسبه و در قالب JSON بازمیگرداند. در صورت عدم وجود اطلاعات یا بروز خطا، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
- اگر
programmer_id
ارسال نشده باشد، باید Programmer ID is required
را با وضعیت 400 برگرداند.
- اگر
programmer_id
عدد نباشد یا کمتر از ۱ باشد، باید Invalid programmer ID
را با وضعیت 400 برگرداند.
- اگر برنامهنویس با
programmer_id
دادهشده وجود نداشته باشد، باید Programmer not found
را با وضعیت 404 برگرداند.
پاسخ موفقیتآمیز:
- محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
محاسبه حقوق: حقوق بر اساس فرمول زیر محاسبه میشود:
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to calculate salary
+ پیام خطا
هندلر CreateProgrammerHandler
توضیح:
این هندلر برای ایجاد یک برنامهنویس جدید در سیستم است. ابتدا دادههای ورودی را بررسی کرده و در صورت معتبربودن، برنامهنویس را در پایگاهداده ثبت میکند. در صورت بروز خطا یا نامعتبربودن اطلاعات، پیام خطای مناسب ارسال میشود.
اعتبارسنجی:
- اگر
name
ارسال نشده باشد، باید Invalid input format
را با وضعیت 400 برگردانید.
- اگر فیلد
name
خالی باشد، باید Name is required
را با وضعیت 400 برگردانید.
پاسخ موفقیتآمیز:
پاسخ خطا:
- وضعیت: 500 با پیام
Failed to create programmer
+ پیام خطا
- در تمام هندلرها اگر خطای داخلی سرور رخ دهد، باید وضعیت 500 برگردانده شود.
توابع Repository🔗
متدهای Repository زیر را پیادهسازی کنید:
متد CreateAttendance
رکورد Attendance
جدیدی با تمام فیلدهای الزامی ایجاد میکند
در صورت وجود attendance
تکراری (با همان ProgrammerID
و Date
) خطا برمیگرداند
آیدی تولید شده برای رکورد جدید باید غیرصفر باشد
در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند
متد GetAttendancesByProgrammer
لیست تمام رکوردهای حضور و غیاب یک برنامهنویس را برمیگرداند
نتایج باید بر اساس تاریخ (date
) به صورت نزولی مرتب شوند
حداکثر ۱۲ رکورد بازگردانده شود
در صورت عدم وجود رکورد، لیست خالی برمیگرداند
در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند
متد GetAllAttendance
لیست تمامی رکوردهای حضور و غیاب موجود در پایگاهداده را برمیگرداند.
در صورت عدم وجود رکورد، لیست خالی برمیگرداند.
در صورت خطا، خطا را برمیگرداند.
متد UpdateAttendance
رکورد موجود را با مقادیر جدید بهروزرسانی میکند.
فیلدهای قابل بهروزرسانی: date
، check_in
و check_out
در صورت عدم وجود رکورد مرتبط، خطا برمیگرداند.
در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
متد DeleteAttendance
تمام رکوردهای حضور و غیاب مربوط به یک برنامهنویس را حذف میکند.
پس از حذف، هیچ رکوردی با ProgrammerID مربوطه نباید وجود داشته باشد.
در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
متد DeleteOneDayAttendance
رکورد حضور و غیاب مربوط به یک روز خاص را حذف میکند.
تاریخ باید دقیقاً مطابقت داشته باشد (برای انجام این کار تمامی رکوردهای تاریخ آن روز را از ساعت 00:00:00
تا ساعت 24:00:00
حذف کنید. میتوانید از date.Truncate
استفاده کنید)
در صورت عدم وجود رکورد، خطا برمیگرداند.
در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
متد GetGirinofReport
گزارش روزانه شامل مجموع تاخیرها (total_delay_minutes
) و خروجهای زودهنگام (total_early_departure_minutes
) را همراه با programmer_id
ایجاد میکند.
محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
در صورت عدم وجود رکورد در تاریخ مشخص، خطا برمیگرداند.
در صورت خطا در محاسبات یا عملیات پایگاهداده، خطا را برمیگرداند.
متد GetMonthlyReport
گزارش ماهانه شامل آمار حضور، اضافهکاری و تاخیرها را در بازه زمانی داده شده ایجاد میکند
محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
این گزارش شامل آیدی برنامهنویس (programmer_id
) تعداد روزهای حضور (total_days_present
)، مجموع دقیقههای اضافهکاری (total_overtime_minutes
)، مجموع دقیقههای تاخیر (total_delay_minutes
) و مجموع دقیقههای خروج زودهنگام (total_early_departure_minutes
) است.
در صورت خطا در محاسبات یا عملیات پایگاهداده، خطا را برمیگرداند.
متد CalculateSalary
حقوق (SalaryReport
) یک برنامهنویس خاص را در سی روز گذشته محاسبه میکند.
محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.
حقوق خالص را بر اساس فرمول زیر محاسبه میکند:
حقوق پایه = تعداد روزهای حضور × نرخ روزانه (DailyRate
)
اضافهکاری = مجموع دقیقههای اضافهکاری × نرخ اضافهکاری (OvertimeRate
)
جریمه تاخیر = مجموع دقیقههای تاخیر × نرخ جریمه (DelayPenalty
)
حقوق نهایی = حقوق پایه + اضافهکاری - جریمه تاخیر
این گزارش شامل نام برنامهنویس (name
)، تعداد روزهای حضور (total_days_present
)، مجموع دقیقههای اضافهکاری (total_overtime_minutes
)، مجموع دقیقههای تاخیر (total_delay_minutes
)، مجموع دقیقههای خروج زودهنگام (total_early_departure_minutes
) و مجموع حقوق (total_salary
) است.
در صورت خطا در محاسبات یا عملیات پایگاهداده، خطا را برمیگرداند
متد CreateProgrammer
- یک برنامهنویس جدید ایجاد میکند.
- آیدی تولید شده باید غیرصفر باشد.
- در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
متد GetProgrammerByID
اطلاعات کامل برنامهنویس را بر اساس ID برمیگرداند.
در صورت عدم وجود برنامهنویس با ID داده شده، خطا برمیگرداند.
در صورت خطا در عملیات پایگاهداده، خطا را برمیگرداند.
متد IsExistDateAndProgrammerID
بررسی میکند که آیا رکورد حضور و غیاب برای یک برنامهنویس خاص در تاریخ مشخص وجود دارد یا خیر.
تاریخ باید دقیقاً مطابقت داشته باشد (برای انجام این کار تمامی رکوردهای تاریخ آن در آن روز یعنی از ساعت 00:00:00
تا ساعت 24:00:00
را بررسی کنید)
در صورت وجود، رکورد کامل را همراه با true
برمیگرداند.
در صورت عدم وجود، nil
و false
برمیگرداند.
تاریخ باید دقیقاً مطابقت داشته باشد (با در نظر گرفتن ساعت ۰۰:۰۰:۰۰)
نکات
شما باید در این سوال از GORM و ویژگیهای آن استفاده کنید و به جز پکیجهای موجود در پروژه اولیه اجازه استفاده از پکیج دیگری را ندارید.
فایل db.go
برای اتصال GORM به پایگاهداده با استفاده از الگوی طراحی singleton است. شما نیازی به تغییر آن ندارید به جز تغییر ثابتها برای تست روی محیط لوکال خود.
فایلها و کدهای پروژهاولیه را نباید تغییر بدهید و صرفا باید مکانهایی که با کامنت // TODO Implement
برای شما مشخص شده را پیادهسازی کنید.
نیازی به ارسال فایلهای go.mod
و go.sum
ندارید!
چه چیزی را آپلود کنید
پس از پیادهسازی ویژگیهای خواستهشده، فقط دایرکتوری app
که شامل سه فایل handlers.go
و models.go
و repository.go
است را فشرده کرده و صورت زیپ ارسال کنید.
فایلهایی که نیاز به ویرایش دارند:
app/models.go
app/handlers.go
app/repository.go
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.