88 lines
3.3 KiB
Markdown
88 lines
3.3 KiB
Markdown
# لاگ یکپارچهٔ پیامک خروجی در دیالوگ ارسال
|
|||
|
|
|
||
|
|
تاریخ: ۲۰۲۶-۱۰-۱۷
|
||
|
|
وضعیت: تأیید شده؛ آمادهٔ پلن پیادهسازی.
|
||
|
|
|
||
|
|
## هدف
|
||
|
|
|
||
|
|
در دیالوگ ارسال پیامک (لید و شخص)، لیست پیامکهایی که از این به بعد برای همان مخاطب رفته نمایش داده شود: دستی، کمپین گروهی، اتوماسیون.
|
||
|
|
|
||
|
|
## تصمیمها
|
||
|
|
|
||
|
|
| موضوع | تصمیم |
|
||
|
|
| --- | --- |
|
||
|
|
| منبع حقیقت | جدول جدید `OutboundSmsLog` |
|
||
|
|
| تاریخچهٔ قبل از استقرار | مهم نیست؛ فقط از این به بعد |
|
||
|
|
| پوشش | manual + campaign + automation |
|
||
|
|
| UI | همان `SmsComposeModal` برای لید و شخص |
|
||
|
|
|
||
|
|
## مدل داده
|
||
|
|
|
||
|
|
`OutboundSmsLog`:
|
||
|
|
|
||
|
|
- `id`, `organizationId`
|
||
|
|
- `personId` اختیاری، `leadId` اختیاری (حداقل یکی در عمل پر میشود)
|
||
|
|
- `mobile`, `body` (متن نهایی پس از توکن/لینک)
|
||
|
|
- `source`: `manual` | `campaign` | `automation`
|
||
|
|
- `sourceId` اختیاری (شناسهٔ کمپین / قانون اتوماسیون)
|
||
|
|
- `status`: `sent` | `failed`
|
||
|
|
- `error` اختیاری
|
||
|
|
- `actorUserId` اختیاری (برای دستی)
|
||
|
|
- `createdAt`
|
||
|
|
- ایندکس: `(organizationId, personId, createdAt)`, `(organizationId, leadId, createdAt)`
|
||
|
|
|
||
|
|
## نقاط ثبت
|
||
|
|
|
||
|
|
1. `PeopleService.sendSms` — source=`manual`
|
||
|
|
2. `LeadsService.sendSms` — source=`manual` (LeadEvent فعلی میتواند بماند)
|
||
|
|
3. `SmsCampaignsService.processCampaign` پس از هر تلاش ارسال — source=`campaign`, `sourceId`=campaignId
|
||
|
|
4. ارسال SMS در message-automations — source=`automation`, `sourceId`=ruleId، با `personId` وقتی موجود است
|
||
|
|
|
||
|
|
ثبت نباید ارسال را بشکند: اگر لاگ fail شد، فقط log خطا؛ نتیجهٔ SMS حفظ شود.
|
||
|
|
|
||
|
|
## API
|
||
|
|
|
||
|
|
- `GET /people/:id/sms-history?limit=50`
|
||
|
|
- `GET /leads/:id/sms-history?limit=50`
|
||
|
|
|
||
|
|
`OutboundSmsHistoryResponse`:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
{
|
||
|
|
items: Array<{
|
||
|
|
id: string;
|
||
|
|
body: string;
|
||
|
|
mobile: string;
|
||
|
|
source: 'manual' | 'campaign' | 'automation';
|
||
|
|
sourceLabel: string; // دستی | کمپین | اتوماسیون
|
||
|
|
status: 'sent' | 'failed';
|
||
|
|
createdAt: string;
|
||
|
|
}>;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
مرتبسازی: جدیدترین اول. Scope دسترسی همان دسترسی به شخص/لید.
|
||
|
|
|
||
|
|
## UI
|
||
|
|
|
||
|
|
در `SmsComposeModal`:
|
||
|
|
|
||
|
|
- prop اختیاری `historyEndpoint`
|
||
|
|
- با `open` لود تاریخچه
|
||
|
|
- بلوک «پیامکهای قبلی» زیر پیشنمایش؛ اسکرول؛ خالی = «هنوز پیامکی ثبت نشده»
|
||
|
|
- پس از ارسال موفق، رفرش لیست (یا prepend محلی)
|
||
|
|
|
||
|
|
`LeadDetailView` و `PersonProfileView` endpoint تاریخچه را پاس میدهند.
|
||
|
|
|
||
|
|
## خارج از محدوده
|
||
|
|
|
||
|
|
- بکفیل تاریخچهٔ قدیمی
|
||
|
|
- نمایش در صفحهٔ جدا / گزارش سازمانی کامل
|
||
|
|
- ویرایش یا حذف لاگ از UI
|
||
|
|
|
||
|
|
## معیار پذیرش
|
||
|
|
|
||
|
|
1. بعد از ارسال دستی به لید/شخص، همان متن در دیالوگ دیده شود.
|
||
|
|
2. پیامک کمپین و اتوماسیون (برای شخص) در تاریخچه ظاهر شود.
|
||
|
|
3. لید و شخص هر دو از همان مودال تاریخچه بگیرند.
|