9.5 KiB
قیمت هر پیامک در تنظیمات + رفع تنظیمات ارسال پیامک کیفپول
پروژه
clinicpro (Backend + Admin SPA).
زمینه
دو مشکل در بخش پیامک پنل ادمین:
/admin/settings→ بخش «پیامک»: امکان تعیین «هزینه هر پیامک» وجود ندارد (قبلاً بود). قیمت هر پیامک اکنون در بکاند هاردکد است:SmsWalletController::SMS_PRICE_RIALS = 500. باید به یک تنظیمِ قابلویرایش در همین صفحه تبدیل شود./admin/sms-wallet→ «تنظیمات ارسال پیامک»: درست کار نمیکند — بعد از ذخیره، وضعیت واقعی سرور (بهویژه وضعیت تأیید متن پیامک بعد از ویزیت) در UI منعکس نمیشود، چون state محلی بعد از ذخیره/رفچ ریست نمیشود.
مشکل / هدف
- افزودن کلید تنظیم
sms_price_rials(قیمت هر پیامک) که در/admin/settingsبخش پیامک قابل ویرایش باشد و بکاند بهجای مقدار هاردکد از آن استفاده کند. - رفع باگِ state کهنه در فرم تنظیمات ارسالِ
/admin/sms-wallet.
فایلهای مرتبط
| فایل | نقش |
|---|---|
src/Config/Controller/SiteConfigController.php |
ALLOWED_KEYS تنظیمات سایت (باید sms_price_rials اضافه شود) |
src/Sms/Controller/SmsWalletController.php |
استفاده از قیمت پیامک (هاردکد) + resolveEntity + endpoint تنظیمات |
assets/admin/pages/SettingsPage.tsx |
فرم تنظیمات؛ بخش sms |
assets/admin/pages/SmsWalletPage.tsx |
فرم «تنظیمات ارسال پیامک» (state محلی) |
docs/api/sms.md |
مستندات (بهروزرسانی) |
وضعیت فعلی
قیمت هاردکد و balance:
// src/Sms/Controller/SmsWalletController.php
private const SMS_PRICE_RIALS = 500;
// ...
$smsPriceRials = self::SMS_PRICE_RIALS;
$estimatedSms = (int) floor($balanceRials / $smsPriceRials);
return $this->success([
'balance_rials' => $balanceRials,
'sms_price_rials' => $smsPriceRials,
'estimated_sms_count' => $estimatedSms,
]);
کلیدهای مجاز تنظیمات (بدون sms_price_rials):
// src/Config/Controller/SiteConfigController.php
private const ALLOWED_KEYS = [
// ...
'sms_panel_fee_rials',
'appointment_fee_rials',
// ...
];
بخش پیامکِ SettingsPage.tsx فقط وضعیت کلید API را نشان میدهد (فیلد قیمت ندارد):
{current.id === 'sms' && (
// فقط sms_api_key_configured نمایش داده میشود
)}
فرم تنظیماتِ SmsWalletPage.tsx — state محلی بعد از ذخیره ریست نمیشود:
const [localSettings, setLocalSettings] = useState<SmsSettings | null>(null);
const currentSettings = localSettings ?? settings;
const saveMutation = useMutation({
mutationFn: (body: SmsSettings) => api.patch('/api/v1/sms/settings', body),
onSuccess: () => { qc.invalidateQueries({ queryKey: ['sms-settings'] }); toast.success('تنظیمات ذخیره شد'); },
// ❌ localSettings ریست نمیشود → currentSettings همان نسخهٔ ویرایششده میماند،
// دادهی تازهٔ سرور (مثل post_visit_text_status = 'pending') نمایش داده نمیشود
});
وظایف
۱. افزودن کلید sms_price_rials به تنظیمات سایت (Backend)
در SiteConfigController::ALLOWED_KEYS کلید 'sms_price_rials' را اضافه کن (کنار sms_panel_fee_rials). با این کار GET/PATCH /api/v1/admin/settings این کلید را برمیگرداند/ذخیره میکند.
۲. استفادهٔ بکاند از قیمتِ قابلتنظیم بهجای هاردکد
در SmsWalletController:
SiteConfigRepositoryرا به constructor تزریق کن (اگر نیست).- یک helper خصوصی بساز:
private function smsPriceRials(): int { return max(1, (int) ($this->configRepo->get('sms_price_rials') ?: self::SMS_PRICE_RIALS)); }وSMS_PRICE_RIALS = 500را بهعنوان fallback نگهدار. - در
balance()بهجایself::SMS_PRICE_RIALSاز$this->smsPriceRials()استفاده کن. - جستجو کن آیا جای دیگری قیمت هر پیامک برای کسر از کیفپول هنگام ارسال استفاده میشود (مثلاً
SmsWalletServiceیا مسیر ارسال پیامک). اگر بله، همانجا هم ازsms_price_rials(config) استفاده شود تا کسر و «تعداد تخمینی» همخوان باشند. اگر جایی مقدار ثابت دیگری هست، آن را هم به config متصل کن.
۳. فیلد «هزینه هر پیامک» در SettingsPage.tsx (Admin)
- به
FormValuesschema وdefaultValuesکلیدsms_price_rialsرا اضافه کن (مثلsms_panel_fee_rials؛ نوع string، پیشفرض مثلاً'500'). - در بخش
current.id === 'sms'یکFieldباinput type="number"برایsms_price_rialsاضافه کن (الگوی دقیقاً مشابه فیلدsms_panel_fee_rialsدر بخش financial):<Field label="هزینه هر پیامک" hint="مبلغ کسرشده از کیف پول به ازای هر پیامک ارسالی (ریال)."> <input {...register('sms_price_rials')} type="number" min={0} dir="ltr" className="input" style={{ maxWidth: 200 }} placeholder="500" /> </Field> - مطمئن شو مقدار اولیه از
data(پاسخ GET/api/v1/admin/settings) خوانده و در PATCH ارسال میشود (چون کلFormValuesارسال میشود، با افزودن به schema/defaults خودکار انجام میشود).
۴. رفع state کهنهٔ فرم تنظیمات در SmsWalletPage.tsx
بعد از ذخیرهٔ موفق، localSettings را ریست کن تا currentSettings به دادهٔ تازهٔ سرور برگردد (وضعیت تأیید متن، مقادیر نرمالشده):
const saveMutation = useMutation({
mutationFn: (body: SmsSettings) => api.patch('/api/v1/sms/settings', body),
onSuccess: () => {
setLocalSettings(null); // ← افزوده شود
qc.invalidateQueries({ queryKey: ['sms-settings'] });
toast.success('تنظیمات ذخیره شد');
},
onError: (e: any) => toast.error(e.message),
});
- همچنین اگر لازم است که با هر بار تازهشدن
settingsDataهم state محلی صفر شود (برای جلوگیری از ماندگاری ویرایشهای ذخیرهنشده پس از رفچ)، یکuseEffect(() => { setLocalSettings(null); }, [settingsData])اضافه کن. - در بدنهٔ PATCH فقط فیلدهای موردنیاز فرستاده شوند (
reminder_enabled,reminder_hours_before,post_visit_enabled,post_visit_text)؛ backend بقیه را نادیده میگیرد ولی برای تمیزی میتوان همینها را صریح فرستاد.
۵. دسترسی صفحهٔ کیفپول برای کاربرانِ بدون entity (بررسی و تصمیم)
SmsWalletController::resolveEntity فقط برای ROLE_DOCTOR و ROLE_CLINIC entity برمیگرداند؛ برای بقیه (از جمله ادمینِ بدون پروفایل، secretary، clinic_doctor) ['unknown', null] → همهٔ endpointها 403 پروفایل یافت نشد میدهند و صفحه «کار نمیکند».
- اگر صفحهٔ
/admin/sms-walletباید برای نقشهای دیگر (مثلclinic_doctor) هم کار کند،resolveEntityرا گسترش بده تا آن نقشها را هم به doctor/clinic نگاشت کند. - اگر کیفپول فقط برای doctor/clinic معنا دارد، در
SmsWalletPage.tsxهنگام خطای 403 یک empty-state مناسب («کیف پول پیامک فقط برای پزشک/کلینیک فعال است») نمایش بده تا صفحه سفید/خراب نشود. - تصمیم را بر اساس نقشهای واقعی پروژه بگیر و در گزارش ذکر کن.
نکات مهم
- کلید تنظیم
sms_price_rialsباید همان واحد ریال باشد؛ balance→count و کسرِ هنگام ارسال باید از یک مقدار بخوانند تا ناسازگاری نشود. SMS_PRICE_RIALS = 500بهعنوان fallback بماند (نصبهای بدون مقدار config نشکنند).- الگوهای موجود پنل:
Field+register(React Hook Form)، پاسخهاdata?.data، تاریخها Unix. - بعد از تغییر Backend/endpoint:
ddev exec php -l ...،ddev exec php bin/console cache:clear،ddev exec php vendor/bin/phpstan analyse src/Sms src/Config؛ و بعد از تغییر TS:ddev exec npx tsc --noEmit --project tsconfig.json. - طبق قانون پروژه،
docs/api/sms.md(و در صورت لزومdocs/api/admin.mdبرای کلید تنظیم جدید) در همین session بهروز شود: افزودنsms_price_rialsبه لیست تنظیمات و توضیح استفادهٔ آن. - Entity تغییر نمیکند → migration لازم نیست (فقط یک ردیف در جدول تنظیمات سایت که با set ساخته میشود).