feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data. - Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS. - Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates. - Develop SendSmsHandler for handling SMS sending messages. - Create SmsService to manage SMS dispatching and logging. - Add UserProfileController for managing user profiles with CRUD operations. - Implement UserProfile entity and repository for user profile data management. - Update symfony.lock and bootstrap.php for project dependencies and environment setup.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# پایگاه داده — تسک ۰۲: ماژول احراز هویت
|
||||
|
||||
## جدول: users
|
||||
_(از بخش ۲.۱ مستند + بررسی DB backup Drupal)_
|
||||
|
||||
| ستون | نوع | نام Drupal | توضیح |
|
||||
|------|-----|-----------|-------|
|
||||
| id | INT AUTO_INCREMENT PK | id | شناسه داخلی |
|
||||
| uuid | CHAR(36) UNIQUE NOT NULL | uuid | شناسه عمومی UUID |
|
||||
| uid | INT FK → users.id NOT NULL | uid | ارجاع به خود جدول (self-reference — در Drupal الزامی) |
|
||||
| mobile_number | VARCHAR(20) UNIQUE NOT NULL | name | شماره موبایل — به عنوان username استفاده میشود |
|
||||
| password | VARCHAR(255) NOT NULL | pass | رمز عبور هششده با bcrypt |
|
||||
| realname | VARCHAR(255) NULL | field_realname | نام و نامخانوادگی کامل (نه first_name/last_name!) |
|
||||
| picture | VARCHAR(500) NULL | user_picture | آدرس تصویر پروفایل |
|
||||
| email | VARCHAR(180) UNIQUE NULL | mail | ایمیل (اختیاری) |
|
||||
| status | TINYINT(1) DEFAULT 1 | status | ۱=فعال، ۰=غیرفعال |
|
||||
| roles | JSON NOT NULL | — | نقشها — مثال: `{"0":"authenticated","2":"doctor"}` |
|
||||
| created_at | INT NOT NULL | created | Unix timestamp — زمان ایجاد |
|
||||
| updated_at | INT NOT NULL | changed | Unix timestamp — آخرین ویرایش |
|
||||
|
||||
> **⚠ مهم:**
|
||||
> - Drupal از `realname` (یک فیلد) استفاده میکند، **نه** `first_name` + `last_name`!
|
||||
> - timestampها نوع **INT** هستند (Unix timestamp)، نه DATETIME
|
||||
> - `status` نوع **TINYINT** است (نه ENUM)
|
||||
> - `uid` self-reference است — در Drupal هر user به خودش اشاره میکند
|
||||
|
||||
## ایندکسها
|
||||
```sql
|
||||
CREATE UNIQUE INDEX idx_users_uuid ON users(uuid);
|
||||
CREATE UNIQUE INDEX idx_users_mobile ON users(mobile_number);
|
||||
CREATE UNIQUE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_status ON users(status);
|
||||
```
|
||||
|
||||
## ذخیرهسازی OTP در Redis (نه پایگاه داده)
|
||||
```
|
||||
کلید: otp:{uuid} ← UUID از /api/v1/user/send-code برگردانده میشود (نه mobile!)
|
||||
مقدار: {"code": "12345", "attempts": 0}
|
||||
TTL: 1200 ثانیه (20 دقیقه)
|
||||
```
|
||||
|
||||
## جریان OTP (MobileGrant)
|
||||
```
|
||||
1. POST /api/v1/user/send-code → {mobile, captcha_token}
|
||||
→ UUID تولید + کد OTP ذخیره در Redis با کلید otp:{uuid}
|
||||
→ UUID برگردانده میشود
|
||||
|
||||
2. POST /api/v1/user/verify-code → {mobile, captcha_token}
|
||||
→ کد از Redis با کلید otp:{uuid} تأیید میشود
|
||||
|
||||
3. POST /oauth/token → grant_type=mobile (MobileGrant)
|
||||
→ JWT token صادر میشود
|
||||
|
||||
4. GET /oauth/userinfo → Bearer token
|
||||
→ اطلاعات کاربر + شیء clinic_pro برگردانده میشود
|
||||
```
|
||||
|
||||
## نمونه پاسخ GET /oauth/userinfo (واقعی از Drupal)
|
||||
```json
|
||||
{
|
||||
"email": null,
|
||||
"email_verified": true,
|
||||
"username": "09120671710",
|
||||
"id": "22",
|
||||
"uuid": "d200f5c5-d717-4526-b263-d3bb7d0228d6",
|
||||
"created": "1762262151",
|
||||
"changed": "1762262267",
|
||||
"status": "1",
|
||||
"roles": {
|
||||
"0": "authenticated",
|
||||
"2": "doctor"
|
||||
},
|
||||
"realName": "single doctor",
|
||||
"picture": [],
|
||||
"clinic_pro": {
|
||||
"base_role": "doctor",
|
||||
"db_uuid": "61be915b-595a-42e5-bca5-f80d22f4f14a",
|
||||
"db_key": "22bea8c1dc64d9b0c744810722519efe7290276ecd82d9bc650482aa4539bf0d",
|
||||
"my_doctors_uuid": {
|
||||
"uuid": "61be915b-595a-42e5-bca5-f80d22f4f14a",
|
||||
"id": "29",
|
||||
"name": "single doctor"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **نکات `/oauth/userinfo`:**
|
||||
> - `realName` با حرف بزرگ N (camelCase)
|
||||
> - `roles` یک object است، نه array: `{"0":"authenticated","2":"doctor"}`
|
||||
> - `clinic_pro.base_role` → نقش اصلی: `doctor`, `clinic`, `doctor_s_secretary`
|
||||
> - `clinic_pro.db_uuid` → UUID موجودیت doctor/clinic در جدول clinic_pro
|
||||
> - فقط نقشهای `doctor`, `clinic`, `doctor_s_secretary` میتوانند با پسورد لاگین کنند
|
||||
|
||||
## روابط با سایر جداول
|
||||
```
|
||||
users → user_profiles (OneToOne) : تسک ۰۳
|
||||
users → doctors (OneToOne) : تسک ۰۵
|
||||
users → clinics (OneToOne) : تسک ۰۶
|
||||
users → appointments (OneToMany) : تسک ۱۰
|
||||
users → payments (OneToMany) : تسک ۱۵
|
||||
users → representations (OneToOne): تسک ۱۶
|
||||
```
|
||||
Reference in New Issue
Block a user