تیم فنی اسنپ قصد دارد تا سیستم جدیدی برای مدیریت دستاوردهای کاربران و دادن امتیاز به آنها با بهرهگیری از gamification پیادهسازی کند، اما به دلیل درخواست این تیم برای جذب نیروی فنی، آنها این تسک را به برخی از افرادی سپردهاند که در مرحلهی اول مصاحبهی اسنپ قبول شدهاند. مهدی که سرش شلوغ است، اما میخواهد در اسنپ استخدام شود، از شما میخواهد تا این تسک را برایش انجام دهید. نسخهی اولیهی این سیستم قرار است یک API ساده باشد.
جزئیات پروژه
پروژهی اولیه را از این لینک دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
snapp_baz
├── achievements
│ └── achievements.go
├── db
│ └── connection.go
├── handlers
│ └── achievements.go
├── test
│ └── achievements_sample_test.go
├── go.mod
├── go.sum
└── main.go
پایگاه داده
در این پروژه از پایگاه دادهی PostgreSQL استفاده شده است. برنامه شامل جداول زیر است:
- کاربران (
users
):
نام ستون | نوع | تعریف | ملاحضات |
---|---|---|---|
id |
BIGSERIAL |
شناسهی کاربر | PRIMARY KEY |
name |
VARCHAR(255) |
نام | |
phone |
VARCHAR(255) |
شماره تلفن |
- دستاوردها (
achievements
):
نام ستون | نوع | تعریف | ملاحظات |
---|---|---|---|
id |
BIGSERIAL |
شناسهی دستاورد | PRIMARY KEY |
title |
VARCHAR(255) |
عنوان دستاورد |
- دستاوردهای کاربران (
user_achievements
):
نام ستون | نوع | تعریف |
---|---|---|
user_id |
BIGINT |
شناسهی کاربر |
achievement_id |
BIGINT |
شناسهی دستاورد |
دسترسی به پایگاه داده از طریق تابع db.GetConnection
صورت میگیرد که به شکل singleton پیادهسازی شده است.
منطق کسب دستاوردها
یک map[int](func(int) bool)
به شکل singleton در برنامه تعریف شده که از طریق تابع achievements.GetMap
قابل دسترسی است. این مپ، نگاشتی از شناسهی دستاوردها به توابعی است که با دریافت شناسهی یک کاربر، مشخص میکنند که آیا آن دستاورد توسط کاربر کسب شده است یا خیر.
روتها
نسخهی اولیهی این API شامل روتهای زیر است:
/is_achieved
: این روت به تابعhandlers.IsAchieved
مپ شده است که با دریافت یکuser_id
و یکachievement_id
از query string، مشخص میکند که آیا دستاورد توسط کاربر ذکرشده کسب شده است یا خیر.- اگر پارامتر
user_id
مقداردهی نشده باشد، کد پاسخ باید400
و بدنهی پاسخ باید{"error": "no user id provided"}
باشد. - اگر پارامتر
user_id
مقداردهی شده باشد، اما پارامترachievement_id
مقداردهی نشده باشد، کد پاسخ باید400
و بدنهی پاسخ باید{"error": "no achievement id provided"}
باشد. - اگر مقدار پارامتر
user_id
عددی نباشد، کد پاسخ باید400
و بدنهی پاسخ باید{"error": "invalid user id"}
باشد. - اگر مقدار پارامتر
user_id
عددی باشد، اما مقدار پارامترachievement_id
عددی نباشد، کد پاسخ باید400
و بدنهی پاسخ باید{"error": "invalid achievement id"}
باشد. - اگر کاربری با شناسهی
user_id
در جدولusers
موجود نباشد، کد پاسخ باید404
و بدنهی پاسخ باید{"error": "user not found"}
باشد. - اگر کاربری با شناسهی
user_id
در جدولusers
موجود باشد، اما دستاوردی با شناسهیachievement_id
در جدولachievements
موجود نباشد، کد پاسخ باید404
و بدنهی پاسخ باید{"error": "achievement not found"}
باشد. - اگر هیچ یک از شرایط فوق برقرار نباشد، کد پاسخ باید
200
و بدنهی پاسخ باید{"is_achieved": true}
یا{"is_achieved": false}
باشد (بسته به این که کاربر دستاورد را کسب کرده است یا خیر).
- اگر پارامتر
/get_achievements
: این روت به تابعhandlers.GetAchievements
مپ شده است که با دریافت یکuser_id
از query string، لیست دستاوردهای کاربر را برمیگرداند.- اگر پارامتر
user_id
مقداردهی نشده باشد، کد پاسخ باید400
و بدنهی پاسخ باید{"error": "no user id provided"}
باشد. - اگر مقدار پارامتر
user_id
عددی نباشد، کد پاسخ باید400
و بدنهی پاسخ باید{"error": "invalid user id"}
باشد. - اگر کاربری با شناسهی
user_id
در جدولusers
موجود نباشد، کد پاسخ باید404
و بدنهی پاسخ باید{"error": "user not found"}
باشد. - اگر هیچ یک از شرایط فوق برقرار نباشد، کد پاسخ باید
200
و بدنهی پاسخ باید به فرم{"achievements": ["achievement 1 name", "achievement 2 name"]}
باشد (نام دستاوردها باید بهترتیب صعودی شناسهشان باشد).
- اگر پارامتر
/refresh_achievements
: این روت به تابعhandlers.RefreshAchievements
مپ شده است که با دریافت یکuser_id
از query_string، لیست دستاوردهای کاربر را بهروز میکند. در واقع، دستاوردهایی که تاکنون توسط کاربر کسب نشدهاند باید مجدداً بررسی شوند و در صورتی که دستاورد جدیدی کسب شده بود، در جدولuser_achievements
درج شود.- کد و بدنهی پاسخ این روت باید مشابه روت
/get_achievements
باشد، با این تفاوت که دستاوردهای کسبنشده مجدداً بررسی میشوند.
- کد و بدنهی پاسخ این روت باید مشابه روت
توجه: مقدار هدر Content-Type
در پاسخ همهی درخواستها باید برابر با application/json
قرار داده شود.
آنچه باید آپلود کنید
پس از پیادهسازی برنامه، یک فایل زیپ آپلود کنید که وقتی آن را باز میکنیم، با ساختار زیر مواجه شویم:
snapp_baz
├── handlers
│ └── achievements.go
├── go.mod (Optional)
└── go.sum (Optional)
ارسال پاسخ برای این سؤال