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,133 @@
|
||||
# نکات پیادهسازی — تسک ۱۶: ماژول Representation (داشبورد دکتر)
|
||||
|
||||
## مهم: این ماژول دو نقش دارد
|
||||
۱. **مدیریت نمایندگی (Multi-tenant)** — هر نماینده یک دامنه دارد
|
||||
۲. **داشبورد دکتر** — آمار نوبتها، درآمد، بیماران
|
||||
|
||||
## ساختار Representation در Drupal
|
||||
```php
|
||||
// فیلدهای entity (clinic_pro, bundle=representation):
|
||||
field_domain_name // دامنه سایت نماینده (مثل: http://yasuj-nobat.localhost:3000/)
|
||||
field_city // entity reference به category (شهر)
|
||||
field_active // boolean
|
||||
field_commission_percent // درصد کمیسیون
|
||||
|
||||
// دکتر به نماینده از طریق field_representation لینک میشود
|
||||
// نوبت هم با field_representation لینک میشود (از HTTP Host)
|
||||
```
|
||||
|
||||
## شناسایی نماینده از Host
|
||||
```php
|
||||
// در زمان ثبت نوبت:
|
||||
$host = 'http://yasuj-nobat.localhost:3000/';
|
||||
$representation = $this->representationRepo->findByDomainName($host);
|
||||
// در Symfony: $request->getSchemeAndHttpHost() . '/'
|
||||
```
|
||||
|
||||
## endpoint: my-doctor (برای بیمار)
|
||||
```
|
||||
GET /api/v1/representation/my-doctor/{userId}
|
||||
```
|
||||
→ لیست دکترهایی که این کاربر نوبت گرفته را برمیگرداند.
|
||||
فیلد search: `field_representation = $representationId`
|
||||
|
||||
## endpoint: my-appointments (برای نماینده)
|
||||
```
|
||||
GET /api/v1/representation/my-appointments/{representationId}
|
||||
```
|
||||
→ تمام نوبتهای مرتبط با این نماینده
|
||||
کوئری روی `appointment.field_representation = $id`
|
||||
|
||||
## endpoint: filter (داشبورد ماهانه)
|
||||
```
|
||||
GET /api/v1/representation/filter/{representationId}?timestamp=...
|
||||
```
|
||||
→ از `timestamp` برای تعیین ماه جاری شمسی استفاده میکند
|
||||
|
||||
```php
|
||||
// محاسبه بازه ماه جاری شمسی:
|
||||
$persianMonthRange = $jalaliService->getCurrentPersianMonthRange($timestamp);
|
||||
$startTimestamp = $persianMonthRange['start'];
|
||||
|
||||
// آمار پرداختها در این ماه:
|
||||
// field_status = 'received' AND field_representation = $id AND created >= $startTimestamp
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"payments_total": { "total_price": 12500000, "count": 25 },
|
||||
"total_patients": 142,
|
||||
"today_appointments": 8
|
||||
}
|
||||
```
|
||||
|
||||
## endpoint: yearly-income (درآمد سالانه)
|
||||
```
|
||||
GET /api/v1/representation/yearly-income/{id}?timestamp=...
|
||||
```
|
||||
|
||||
این endpoint از تقویم **شمسی (جلالی)** استفاده میکند:
|
||||
```php
|
||||
// محاسبه بازه سال شمسی:
|
||||
$persianYearRange = $jalaliService->getPersianYearRange($timestamp);
|
||||
$jalaliYear = $persianYearRange['year'];
|
||||
|
||||
// Loop از ماه ۱ تا ۱۲ شمسی:
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$monthRange = $this->getMonthRangeByYearAndMonth($jalaliYear, $month);
|
||||
$income = $this->getMonthIncome($representationId, $monthRange['start'], $monthRange['end']);
|
||||
// income = SUM(payment.amount) WHERE status='received' AND representation=$id AND created BETWEEN start AND end
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"year": 1403,
|
||||
"monthly_income": [
|
||||
{ "month": 1, "income": 8500000 },
|
||||
{ "month": 2, "income": 9200000 },
|
||||
...
|
||||
{ "month": 12, "income": 0 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## JalaliDateService پیادهسازی در Symfony
|
||||
از کد `custom_service/src/jalali/JalaliDateService.php` برای پیادهسازی استفاده کن.
|
||||
توابع مورد نیاز:
|
||||
```php
|
||||
class JalaliDateService {
|
||||
public function getCurrentPersianMonthRange(?int $timestamp): array;
|
||||
// return: ['start' => timestamp, 'end' => timestamp]
|
||||
|
||||
public function getPersianYearRange(?int $timestamp): array;
|
||||
// return: ['year' => int, 'start' => timestamp, 'end' => timestamp]
|
||||
|
||||
public function getMonthRangeByYearAndMonth(int $year, int $month): array;
|
||||
// return: ['start' => timestamp, 'end' => timestamp]
|
||||
|
||||
public function gregorianToPersian(int $gy, int $gm, int $gd): array;
|
||||
// return: [$jy, $jm, $jd]
|
||||
|
||||
public function persianToGregorian(int $jy, int $jm, int $jd): array;
|
||||
// return: [$gy, $gm, $gd]
|
||||
}
|
||||
```
|
||||
|
||||
## کشینگ آمار داشبورد
|
||||
آمار داشبورد را با Redis کش کن (TTL = 5 دقیقه):
|
||||
```php
|
||||
$cacheKey = "dashboard_representation_{$representation->getId()}";
|
||||
// بعد از هر payment جدید → cache invalidate
|
||||
```
|
||||
|
||||
## مجوزها
|
||||
```
|
||||
GET /representation/{uuid} → دکتر مرتبط یا ROLE_ADMIN
|
||||
GET /representation/my-appointments/{id} → دکتر/نماینده یا ROLE_ADMIN
|
||||
GET /representation/my-doctor/{userId} → کاربر خودش
|
||||
GET /representation/filter/{id} → دکتر/نماینده یا ROLE_ADMIN
|
||||
GET /representation/yearly-income/{id} → دکتر/نماینده یا ROLE_ADMIN
|
||||
```
|
||||
Reference in New Issue
Block a user