66 KiB
مشخصات کامل فنی و محصول
سیستم تیکتینگ مشتریان و کارشناسان فروش
نسخه سند: 1.0
نوع پروژه: Web Application
معماری: Django Modular Monolith
Backend: Django + PostgreSQL
Frontend: Django Templates + HTMX + Alpine.js + Vanilla JavaScript + Tailwind CSS
Deployment: Docker / Docker Compose / Linux
File Storage: Local Server Storage
Real-time: نیاز نیست
Redis: نیاز نیست در نسخه اول
Celery: نیاز نیست در نسخه اول
WebSocket: نیاز نیست
Microservice: ممنوع/غیرضروری در V1
1. هدف پروژه
هدف، طراحی و پیادهسازی یک سیستم تیکتینگ ساده، سریع، امن و قابل توسعه برای ارتباط مشتریان با کارشناسان فروش شرکت است.
مشتریان عمدتاً برای مواردی مانند:
- استعلام قیمت
- درصد تخفیف
- شرایط فروش
- موجودی
- شرایط ارسال
- درخواست نمایندگی
- مشاوره فروش
- سؤالات مربوط به محصولات
- شکایات
- سایر مسائل فروش
با کارشناسان ارتباط برقرار میکنند.
سیستم نباید تبدیل به یک Messenger دائمی شبیه WhatsApp یا Telegram شود.
هر Ticket باید نماینده یک موضوع یا مسئله مشخص باشد.
مشتری برای موضوع جدید باید Ticket جدید ایجاد کند.
داخل یک Ticket امکان چند مرحله سؤال و جواب وجود دارد، اما فلسفه سیستم بر اساس Ticketing باقی میماند.
2. اهداف اصلی
سیستم باید:
- ورود مشتری با شماره موبایل و OTP را فراهم کند.
- ثبتنام اجباری سنتی نداشته باشد.
- تعیین نام، نام خانوادگی و Password اختیاری باشد.
- مشتری بتواند چند Ticket همزمان داشته باشد.
- مشتری بتواند کارشناس موردنظر خود را انتخاب کند.
- در صورت عدم انتخاب کارشناس، Ticket وارد صف عمومی کارشناسان شود.
- کارشناس بتواند Ticket عمومی را برای خودش بردارد.
- بعد از برداشتن Ticket، اطلاعات آن از سایر کارشناسان مخفی شود.
- مدیران مجاز همیشه بتوانند همه Ticketها را مشاهده کنند.
- امکان پاسخ متنی و Attachmentهای مختلف وجود داشته باشد.
- Voice مستقیماً داخل مرورگر ضبط شود.
- امکان Edit پیام وجود داشته باشد.
- تاریخچه Edit کاملاً حفظ شود.
- حذف پیام کارشناس به صورت Soft Delete باشد.
- مدیر بتواند متن اصلی پیام حذفشده را مشاهده کند.
- امکان Internal Note وجود داشته باشد.
- امکان Transfer Ticket وجود داشته باشد.
- امکان Share کردن Ticket با Expert دیگر وجود داشته باشد.
- امکان Mention کارشناس وجود داشته باشد.
- Notification مشتری از طریق SMS انجام شود.
- Notification کارشناسان از طریق Dashboard و Bale انجام شود.
- مشتری از طریق Magic Link بتواند سریع Ticket را باز کند.
- Permission System منعطف باشد.
- Priority و SLA قابل استفاده باشند.
- فایلها روی Storage محلی Server ذخیره شوند.
3. محدوده کاربران
حداکثر بار مورد انتظار در نسخه فعلی:
- حدود 200 مشتری
- حداکثر 20 کارشناس فروش
- یک شرکت
- بدون Multi-Tenancy
- حجم پیام و Ticket نسبتاً پایین
در نتیجه نیازی به معماری پیچیده Distributed وجود ندارد.
4. معماری کلان
معماری سیستم باید:
Modular Monolith
باشد.
تمام Business Logic داخل یک Django Project قرار میگیرد ولی Domainها به Appهای منطقی جدا تقسیم میشوند.
ساختار پیشنهادی:
project/
│
├── config/
│ ├── settings/
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
│
├── apps/
│ │
│ ├── accounts/
│ ├── customers/
│ ├── staff/
│ ├── tickets/
│ ├── messaging/
│ ├── attachments/
│ ├── notifications/
│ ├── integrations/
│ │ ├── sms/
│ │ └── bale/
│ │
│ └── core/
│
├── templates/
├── static/
├── media/
├── tests/
│
├── docker/
├── docker-compose.yml
├── Dockerfile
└── manage.py
5. معماری لایهای داخل Appها
Business Logic نباید مستقیماً داخل Viewها نوشته شود.
ساختار پیشنهادی برای Appهایی مثل tickets:
tickets/
├── models.py
├── forms.py
├── urls.py
├── selectors.py
├── policies.py
├── services/
│ ├── create_ticket.py
│ ├── claim_ticket.py
│ ├── transfer_ticket.py
│ ├── ticket_access.py
│ └── status.py
├── views/
├── admin.py
└── tests/
Responsibility
views
فقط:
- HTTP
- Forms
- Redirect
- HTMX response
را مدیریت کند.
services
Business Logic را اجرا کند.
selectors
Queryهای خواندن اطلاعات را مدیریت کند.
policies
Permission و authorization را مدیریت کند.
6. اصل بسیار مهم Authorization
هیچ Permission مهمی نباید فقط در:
- Template
- JavaScript
- HTMX
- CSS
اعمال شود.
تمام Permissionها باید Server Side کنترل شوند.
مخفیکردن یک Button در UI به معنی جلوگیری از عملیات نیست.
7. User Model
از ابتدای پروژه باید Custom User Model ایجاد شود.
بعداً تغییر AUTH_USER_MODEL بسیار هزینهبر است.
مدل مفهومی:
User
---------
id
public_id
username
mobile
email
password
account_type
is_active
is_staff
created_at
updated_at
last_login
account_type:
CUSTOMER
STAFF
کاربر Customer میتواند Password نداشته باشد.
در این حالت:
user.set_unusable_password()
استفاده شود.
8. CustomerProfile
CustomerProfile
----------------
user
first_name
last_name
customer_type
created_at
updated_at
customer_type اختیاری است.
مقادیر اولیه:
PERSON
CONSUMER
SHOP
REPRESENTATIVE
COMPANY
OTHER
نمایش فارسی:
شخص
مصرفکننده نهایی
فروشگاه
نمایندگی
شرکت
سایر
9. StaffProfile
StaffProfile
-------------
user
display_name
avatar
mobile
bale_chat_id
bale_notifications_enabled
is_customer_selectable
sort_order
is_active
created_at
updated_at
فقط کارشناسانی که:
is_customer_selectable = True
دارند در صفحه انتخاب کارشناس به مشتری نمایش داده شوند.
10. احراز هویت مشتری
روش اصلی:
Mobile
↓
OTP
↓
Login
Password اجباری نیست.
پس از ورود موفق، سیستم میتواند پیشنهاد دهد:
برای ورود آسانتر در آینده میتوانید رمز عبور تعیین کنید.
ولی این درخواست نباید Blocking باشد.
11. ورود با Password
در صورت تعیین Password، مشتری میتواند در آینده با:
mobile + password
هم وارد شود.
با این حال روش OTP همچنان باید در صفحه Login در اولویت UX باشد.
12. Staff Login
کارشناسان و مدیران صفحه Login مستقل داشته باشند.
مثلاً:
/staff/login/
Customer login:
/login/
Staff Login با Username/Password انجام شود.
13. OTP Architecture
سیستم SMS نباید وابسته به Provider خاص باشد.
از Adapter Pattern و Protocol استفاده شود.
Interface مفهومی:
class SMSProvider(Protocol):
def send_otp(
self,
mobile: str,
code: str,
) -> SMSResult:
...
def send_message(
self,
mobile: str,
message: str,
) -> SMSResult:
...
Providerهای آینده میتوانند شامل:
KavenegarProvider
SMSIrProvider
IPPanelProvider
FarazSMSProvider
CustomSMSProvider
باشند.
Business Logic حق ندارد مستقیم API Provider را Call کند.
14. OTPChallenge Model
OTPChallenge
-------------
id
mobile
purpose
code_hash
expires_at
attempt_count
max_attempts
verified_at
created_at
request_ip
user_agent
Raw OTP نباید داخل Database ذخیره شود.
15. تنظیمات OTP
مقادیر اولیه پیشنهادی:
OTP Length: 6 digits
Expire: 2 minutes
Resend cooldown: 60 seconds
Max verification attempts: 5
تمام موارد باید از Settings قابل تغییر باشند.
Rate Limiting نیز باید برای:
- mobile
- IP
اعمال شود.
Redis برای این حجم کاربر لازم نیست.
Rate Limit میتواند با PostgreSQL انجام شود.
16. شماره موبایل
شمارهها باید قبل از ذخیره Normalize شوند.
برای مثال:
09121234567
به شکل Canonical:
+989121234567
ذخیره شود.
تمام جستجو و Unique Constraint بر اساس شماره Normalizeشده باشد.
17. TicketCategory
Category در Database ذخیره شود.
TicketCategory
---------------
id
title
slug
description
icon
sort_order
is_active
created_at
updated_at
مثال:
استعلام قیمت
درصد تخفیف
شرایط فروش
موجودی
شرایط ارسال
درخواست نمایندگی
شکایت
مشاوره
سایر
انتخاب Category برای Customer اجباری است.
Category غیرفعال برای Ticketهای قدیمی باقی بماند ولی در Ticket جدید نمایش داده نشود.
18. Ticket Model
مدل اصلی:
Ticket
----------------------
id
public_id
ticket_number
customer
category
subject
status
priority
selected_expert
owner
origin_visibility
created_at
updated_at
last_activity_at
first_response_at
last_customer_message_at
last_staff_message_at
first_response_due_at
resolution_due_at
resolved_at
closed_at
reopened_at
19. Ticket Number
Ticket باید شماره قابل خواندن داشته باشد.
مثلاً:
TK-1405-001254
اما Permission و URL نباید بر اساس این شماره امنیت ایجاد کنند.
URLها از public_id یا UUID استفاده کنند.
مثلاً:
/tickets/9d7b.../
20. Subject
Subject اجباری است.
محدوده پیشنهادی:
5 تا 200 character
نمونه:
استعلام قیمت لوله 110
21. اولین پیام Ticket
Ticket علاوه بر Subject باید یک Initial Message داشته باشد.
Initial Message میتواند شامل:
- Text
- Attachment
- Text + Attachments
باشد.
UX باید مشتری را تشویق کند مسئله را کامل توضیح دهد.
22. Ticket Status
Statusهای اصلی:
NEW
OPEN
WAITING_CUSTOMER
WAITING_STAFF
RESOLVED
CLOSED
REOPENED
23. معنی Statusها
NEW
Ticket ایجاد شده ولی هنوز رسیدگی واقعی شروع نشده است.
OPEN
کارشناس Ticket را برداشته یا رسیدگی به آن را شروع کرده است.
WAITING_CUSTOMER
کارشناس پاسخ داده و اکنون انتظار پاسخ مشتری وجود دارد.
WAITING_STAFF
مشتری پاسخ داده و سیستم منتظر پاسخ کارشناس است.
RESOLVED
کارشناس مسئله را حلشده اعلام کرده است.
CLOSED
Ticket بسته شده است.
REOPENED
Ticket بسته یا Resolve شده بوده و Customer دوباره پیام داده است.
24. Reopen
اگر Ticket:
RESOLVED
یا:
CLOSED
باشد و Customer پیام جدید ارسال کند:
Ticket خودکار به:
REOPENED
تغییر وضعیت دهد.
این عملیات باید Transactional باشد.
25. Priority
Priorityهای اولیه:
NORMAL
IMPORTANT
URGENT
Default:
NORMAL
Customer اجازه تغییر Priority ندارد.
کارشناس یا کاربران دارای Permission میتوانند Priority را تغییر دهند.
26. منطق انتخاب Expert توسط Customer
سه حالت مهم وجود دارد.
حالت اول: Customer Expert انتخاب میکند
مثلاً Customer:
احمد
را انتخاب میکند.
در این حالت:
selected_expert = Ahmad
owner = NULL
و فقط:
- Ahmad
- مدیران دارای دسترسی کامل
میتوانند Ticket را مشاهده کنند.
سایر کارشناسان نباید حتی محتوای Ticket را ببینند.
این Ticket وارد Public Queue نمیشود.
27. عدم انتخاب Expert
اگر Customer کارشناسی انتخاب نکند:
selected_expert = NULL
owner = NULL
Ticket وارد Public Queue میشود.
تمام کارشناسان مجاز میتوانند:
- عنوان
- محتوا
- Attachment
- Customer
- سابقه مجاز
را مشاهده کنند.
28. Claim / Lock Ticket
در Public Queue هر Expert میتواند:
برداشتن تیکت
یا:
اختصاص به من
را انتخاب کند.
پس:
owner = current_user
خواهد شد.
29. Race Condition در Claim
ممکن است دو Expert همزمان روی:
اختصاص به من
کلیک کنند.
Claim باید با:
transaction.atomic()
و:
select_for_update()
پیادهسازی شود.
فقط اولین Expert موفق شود.
Expert دوم پیام مناسب دریافت کند:
این تیکت لحظاتی قبل توسط «احمد رضایی» برداشته شد.
30. رفتار Ticket بعد از Claim
بعد از Claim:
فقط:
- Owner
- کارشناسان Shared
- مدیران دارای Permission
حق مشاهده محتوای Ticket را دارند.
سایر کارشناسان فقط اطلاعات محدود زیر را ببینند:
Ticket Title
Owner
Locked/Assigned indicator
و نباید قادر باشند:
- Message را بخوانند
- Attachment ببینند
- Customer information ببینند
- Reply کنند
- Internal Note ببینند
31. Ticket انتخابشده توسط Customer
یک تفاوت مهم:
اگر Customer از ابتدا Expert خاصی انتخاب کرده باشد، سایر کارشناسان حتی Metadata آن را در Public Queue نبینند.
این Ticket خصوصی محسوب میشود.
32. TicketAccessGrant
برای دادن دسترسی به Expertهای دیگر مدل جداگانه ایجاد شود.
TicketAccessGrant
------------------
id
ticket
user
granted_by
permission_level
created_at
revoked_at
در V1 میتوان permission_level را ساده نگه داشت:
FULL
ولی فیلد از ابتدا وجود داشته باشد تا بعداً:
VIEW_ONLY
REPLY
FULL
قابل توسعه باشد.
33. Share Ticket
Owner یا Manager دارای Permission بتواند Expert دیگری را به Ticket اضافه کند.
عملیات UI:
اشتراک با کارشناس
پس از Share، Expert موردنظر:
- محتوای Ticket
- پیامها
- فایلها
را مشاهده میکند و در صورت FULL بودن دسترسی پاسخ میدهد.
34. Revoke Access
Owner یا مدیر مجاز بتواند Access یک Expert را حذف کند.
Manager دارای Full Access هرگز توسط Ticket Grant محدود نمیشود.
35. Transfer Ticket
Owner بتواند Ticket را بدون نیاز به تأیید مدیر به Expert دیگری Transfer کند.
مثلاً:
Ahmad → Reza
پس:
owner = Reza
شود.
Transfer باید Transactional باشد.
36. دسترسی Owner قبلی بعد از Transfer
Default Behavior:
پس از Transfer، Owner قبلی دسترسی اختصاصی خود را از دست میدهد.
در صورت نیاز باید به عنوان Shared Expert به Ticket اضافه شود.
این رفتار از نشت ناخواسته اطلاعات جلوگیری میکند.
37. TicketEvent
Audit Log عمومی در V1 لازم نیست.
اما Domain History لازم است.
مدل:
TicketEvent
------------
id
ticket
event_type
actor
metadata
created_at
Eventها:
CREATED
CLAIMED
TRANSFERRED
SHARED
ACCESS_REVOKED
STATUS_CHANGED
PRIORITY_CHANGED
REOPENED
RESOLVED
CLOSED
این مدل برای:
- Timeline
- SLA
- Debug
- تاریخچه Transfer
استفاده میشود.
این سیستم جای Audit Log جامع را نمیگیرد.
38. Permissions
Role names نباید داخل Business Logic Hard-Code شوند.
یعنی کد نباید بگوید:
if user.role == "manager":
بلکه:
user.has_perm(...)
یا Ticket Policy Service استفاده شود.
39. Django Groups
برای ساده نگهداشتن معماری از:
- Django Groups
- Django Permissions
استفاده شود.
مثلاً Groupهای اولیه:
Sales Expert
Sales Manager
Administrator
نام Group Business Logic نیست.
Permission تعیینکننده رفتار است.
40. Permissions پیشنهادی
tickets.view_all_tickets
tickets.view_private_ticket
tickets.claim_ticket
tickets.transfer_ticket
tickets.share_ticket
tickets.change_ticket_status
tickets.change_ticket_priority
tickets.view_customer_history
messaging.add_internal_note
messaging.edit_own_message
messaging.delete_own_message
messaging.view_deleted_message_content
messaging.view_message_revisions
attachments.download_attachment
staff.manage_staff
tickets.manage_categories
attachments.manage_attachment_policy
41. مدیران
مدیرانی که:
tickets.view_all_tickets
دارند باید بدون استثناء تمام Ticketها را مشاهده کنند.
فرقی نمیکند:
- Customer Expert انتخاب کرده باشد.
- Ticket Locked شده باشد.
- Ticket Shared نشده باشد.
- Ticket متعلق به Expert دیگری باشد.
42. TicketPolicyService
Permission Ticket باید در یک نقطه مرکزی باشد.
مثلاً:
TicketPolicy.can_view(user, ticket)
TicketPolicy.can_reply(user, ticket)
TicketPolicy.can_claim(user, ticket)
TicketPolicy.can_transfer(user, ticket)
TicketPolicy.can_share(user, ticket)
TicketPolicy.can_view_deleted_content(user, ticket)
هیچ View نباید منطق Permission را دوباره بنویسد.
43. Message Model
Message
---------------
id
public_id
ticket
author
body
message_type
is_internal
created_at
updated_at
edited_at
deleted_at
deleted_by
44. انواع Message
PUBLIC
INTERNAL_NOTE
Customer فقط PUBLIC را مشاهده میکند.
45. Internal Note
Expert بتواند Note داخلی ایجاد کند.
مثال:
این مشتری تخفیف ویژه درخواست کرده، قبل از اعلام قیمت با مدیر هماهنگ شود.
Internal Note:
- در Timeline کارشناسان نمایش داده شود.
- از پیامهای معمولی کاملاً متمایز باشد.
- Customer هرگز آن را نبیند.
- در SMS Customer اثر نداشته باشد.
46. Edit Message
Customer و Expert بتوانند Message خودشان را Edit کنند.
بعد از Edit:
edited_at
ثبت شود.
در UI:
ویرایش شده
نمایش داده شود.
47. MessageRevision
قبل از تغییر متن، نسخه قبلی ذخیره شود.
MessageRevision
----------------
id
message
body
edited_by
created_at
مثلاً:
نسخه 1:
قیمت 1,200,000 تومان
نسخه جدید:
قیمت 1,250,000 تومان
هر دو باید قابل بازیابی برای Manager مجاز باشند.
48. مشاهده Revision
Customer الزاماً Revision History را نمیبیند.
Manager یا فرد دارای:
messaging.view_message_revisions
میتواند تاریخچه را مشاهده کند.
49. Delete Message
Expert میتواند Message خودش را Delete کند.
اما:
هیچ Hard Delete انجام نشود.
ممنوع:
message.delete()
به جای آن:
deleted_at
deleted_by
ثبت شود.
Body اصلی در Database باقی بماند.
50. نمایش پیام حذفشده
Customer و کارشناسان عادی ببینند:
این پیام توسط فرستنده حذف شده است.
Manager دارای Permission بتواند:
- Original Body
- Attachments
- Author
- Delete time
را مشاهده کند.
51. Attachments روی Message حذفشده
Attachmentهای Message حذفشده نباید از Disk حذف شوند.
مدیر باید همچنان بتواند آنها را مشاهده کند.
52. MessageMention
Mention نباید فقط Regex روی متن باشد.
مدل:
MessageMention
---------------
message
mentioned_user
created_at
در Frontend Autocomplete Expert نمایش داده شود.
53. Mention و Access
Mention بهتنهایی نباید Permission امنیتی را دور بزند.
اگر Expert Mentionشده به Ticket دسترسی ندارد:
سیستم باید ابتدا Access مناسب ایجاد کند یا UI به فرستنده اعلام کند:
برای Mention کردن این کارشناس ابتدا باید دسترسی تیکت به او داده شود.
Owner و Manager میتوانند:
Share + Mention
را در یک Flow انجام دهند.
54. Bale Notification برای Mention
پس از Mention، Expert از طریق Bale Notification دریافت کند.
مثال:
تیکت TK-1405-1254
علی رضایی شما را Mention کرد:
«لطفاً شرایط تخفیف این مشتری را بررسی کنید.»
مشاهده تیکت:
https://support.example.com/...
55. Attachment Model
Attachment
----------------
id
public_id
message
original_name
stored_name
relative_path
attachment_type
mime_type
extension
file_size
uploaded_by
created_at
56. Attachment Types
سیستم باید حداقل پشتیبانی کند:
IMAGE
VIDEO
VOICE
PDF
WORD
EXCEL
ZIP
RAR
OTHER
57. AttachmentPolicy
محدودیتها نباید Hard-Code باشند.
مدل:
AttachmentPolicy
-----------------
attachment_type
is_enabled
max_file_size
max_files_per_message
allowed_extensions
allowed_mime_types
sort_order
updated_at
58. محدودیت اولیه پیشنهادی
این اعداد فقط Default هستند و باید قابل تغییر باشند.
IMAGE 10 MB
VOICE 25 MB
VIDEO 100 MB
PDF 30 MB
WORD 30 MB
EXCEL 30 MB
ZIP 50 MB
RAR 50 MB
59. Multiple Attachments
هر Message باید بتواند چند Attachment داشته باشد.
مثلاً:
متن
+ 4 تصویر
+ یک PDF
+ یک Voice
مجاز باشد.
60. Attachment Composer UI
قبل از ارسال، UI باید نشان دهد:
- Thumbnail تصویر
- نام فایل
- Size
- Type
- Progress
- حذف قبل از ارسال
- Error فایل نامعتبر
61. File Validation
صرفاً Extension کافی نیست.
Server باید بررسی کند:
- Extension
- MIME Type
- File Signature
برای File Signature استفاده از libmagic یا ابزار مشابه توصیه میشود.
62. ZIP و RAR
ZIP/RAR نباید روی Server Extract شوند.
فقط به عنوان Attachment ذخیره شوند.
این موضوع برای جلوگیری از:
- path traversal
- zip bombs
- فایلهای اجرایی داخلی
مهم است.
63. File Name Security
نام واقعی فایل کاربر نباید مستقیماً نام فایل Storage باشد.
مثلاً:
invoice.pdf
روی Disk به:
1b3ef72c-....pdf
تبدیل شود.
original_name فقط Metadata است.
64. Media Storage
Storage کاملاً Local Server است.
ساختار پیشنهادی:
/media/
└── tickets/
└── 2026/
└── 09/
└── <ticket_uuid>/
└── <message_uuid>/
├── file_uuid.jpg
├── file_uuid.pdf
└── file_uuid.webm
65. Private Media
فایلهای Ticket نباید مستقیماً از Public /media/ قابل دسترسی باشند.
این URL ناامن است:
https://site.com/media/tickets/xxx.pdf
چون Permission را دور میزند.
Download باید ابتدا Django Authorization را بررسی کند.
66. Nginx X-Accel-Redirect
روش پیشنهادی:
- درخواست Download وارد Django میشود.
- Django Permission را بررسی میکند.
- در صورت مجاز بودن Header مخصوص Nginx برگردانده میشود.
- Nginx فایل را Efficient ارسال میکند.
Location فایلها در Nginx باید:
internal
باشد.
67. Voice Recording
Voice با:
MediaDevices.getUserMedia()
MediaRecorder
پیادهسازی شود.
نیازی به Plugin خارجی نیست.
68. Voice UI
Voice Recorder شامل:
Record
Stop
Pause
Resume
Timer
Preview
Delete
Re-record
Send
باشد.
حداقل امکانات:
🎤 ضبط
⏱ مدت ضبط
▶ پخش
🗑 حذف
➤ ارسال
69. Browser Compatibility Voice
Format ضبط ممکن است بین Browserها متفاوت باشد.
مثلاً:
audio/webm
audio/ogg
audio/mp4
Frontend باید با:
MediaRecorder.isTypeSupported()
Format مناسب را انتخاب کند.
Server نباید فقط یک MIME Type را قبول کند.
70. Video
در V1 نیازی به Transcoding Video نیست.
Video همان Format مجاز Upload میشود.
در Browser در صورت پشتیبانی:
<video controls>
نمایش داده شود.
71. Image Preview
Image در Ticket Thumbnail داشته باشد.
با کلیک:
- Modal
- Full preview
نمایش داده شود.
72. PDF
PDF میتواند:
- Download شود.
- در Browser باز شود.
Inline preview اختیاری است.
73. Magic Link
وقتی Expert پاسخ عمومی برای Customer ارسال میکند، Customer توسط SMS مطلع شود.
SMS لینک امن Ticket داشته باشد.
مثلاً:
پاسخ جدیدی برای درخواست شما ثبت شد.
مشاهده:
https://support.example.com/t/AbCDef...
74. Magic Link Security
Token باید حداقل:
256-bit secure random
باشد.
برای تولید:
secrets.token_urlsafe()
یا روش معادل Cryptographically Secure استفاده شود.
75. TicketMagicLink Model
TicketMagicLink
----------------
id
ticket
customer
token_hash
created_at
expires_at
used_at
revoked_at
created_reason
last_used_ip
Raw Token نباید در Database ذخیره شود.
فقط Hash ذخیره شود.
76. Magic Link Lifetime
Magic Link تا:
7 روز
معتبر است.
77. Single-use Magic Link
برای امنیت بهتر، Magic Link باید نقش Bootstrap Login داشته باشد.
Flow:
SMS Link
↓
Token Validation
↓
Customer Session ایجاد میشود
↓
Token مصرف میشود
↓
Redirect به URL تمیز Ticket
یعنی بعد از مصرف، Token داخل Address Bar باقی نماند.
78. Session پس از Magic Link
بعد از ورود با Magic Link، Customer بتواند:
- Ticket را بخواند.
- Reply کند.
- Attachment ارسال کند.
بدون OTP مجدد.
Session میتواند مطابق Policy سیستم تا چند روز معتبر باشد.
79. Magic Link Expired
اگر Link منقضی شده باشد:
Customer به OTP Login هدایت شود.
Destination Ticket حفظ شود.
Flow:
Expired Magic Link
↓
OTP Login
↓
Successful Login
↓
Original Ticket
80. Referrer Security
صفحه Consume Magic Link باید Header مناسب داشته باشد:
Referrer-Policy: no-referrer
تا Token به سایتهای خارجی Leak نشود.
81. SMS Notification Policy
پیشنهاد Default:
بعد از اولین پاسخ عمومی Expert پس از آخرین پیام Customer، SMS ارسال شود.
اگر Expert پشت سر هم 3 پیام ارسال کرد:
سه SMS جداگانه ارسال نشود.
مثلاً:
Customer Message
↓
Expert Message 1 → SMS
Expert Message 2 → No SMS
Expert Message 3 → No SMS
↓
Customer Reply
↓
Expert Message → SMS
این Policy باید قابل تغییر باشد.
82. Notification Architecture
برای External Notification از Database Outbox استفاده شود.
نیازی به Redis یا Celery نیست.
مدل:
NotificationJob
----------------
id
channel
event_type
customer
staff_user
ticket
status
attempts
next_attempt_at
last_error
dedupe_key
created_at
sent_at
Channel:
SMS
BALE
Status:
PENDING
PROCESSING
SENT
FAILED
83. Notification Worker بدون Redis
در Docker Compose یک Process سبک اجرا شود:
python manage.py process_notifications
Worker هر چند ثانیه PostgreSQL را برای Notificationهای Pending بررسی کند.
این Process بخشی از همان Django Project است و Microservice محسوب نمیشود.
84. مزیت Outbox
اگر Bale یا SMS Provider لحظهای Down باشد:
پاسخ Expert همچنان در Database ذخیره میشود.
Notification با Retry ارسال میشود.
در غیر این صورت External API میتواند UX سیستم را خراب کند.
85. OTP متفاوت است
OTP به دلیل اینکه Customer منتظر Code است بهتر است مستقیم ارسال شود.
OTP وارد Outbox عادی نشود.
Provider باید Timeout کوتاه داشته باشد.
86. Bale Adapter
Bale Integration هم نباید در Business Logic مستقیم استفاده شود.
Interface مفهومی:
class StaffNotifier(Protocol):
def send_message(
self,
chat_id: str,
text: str,
) -> NotificationResult:
...
پیادهسازی:
BaleProvider
87. Bale Notification Events
Bale حداقل برای این Eventها استفاده شود:
New Public Ticket
برای کارشناسانی که Notification فعال دارند.
Customer Selected Expert
فقط Expert انتخابشده.
Mention
فقط فرد Mentionشده.
Transfer
Expert مالک جدید.
Share
Expertی که دسترسی دریافت کرده است.
88. Dashboard Notification
بدون WebSocket.
Ticketهای جدید یا نیازمند پاسخ با:
- Badge
- رنگ متفاوت
- Counter
نمایش داده شوند.
HTMX Polling سبک مثلاً هر 30 تا 60 ثانیه برای Counter مجاز است.
WebSocket لازم نیست.
89. Customer Ticket List
Customer Dashboard:
تیکتهای من
هر Card:
Ticket Number
Subject
Category
Status
Last Update
Selected/Assigned Expert
را نمایش دهد.
90. New Ticket Page
Form:
Category *
Subject *
Expert انتخابی
Description / Message
Attachments
Expert Selection با Card:
Avatar
Name
و گزینه:
انتخاب کارشناس خاصی ندارم
91. Ticket Detail مشتری
صفحه شامل:
Ticket header
Status
Category
Expert
Messages
Attachments
Reply form
Voice Recorder
باشد.
Internal Notes هرگز در HTML Customer Rendering نشوند.
92. Customer Profile
صفحه ساده:
نام
نام خانوادگی
نوع مشتری
شماره موبایل
Password
Mobile تغییر مستقیم نداشته باشد مگر با OTP Verification جداگانه.
93. Staff Dashboard
Layout پیشنهادی Desktop:
┌──────────────────────────────────────┐
│ Header / Search / Notifications │
├───────────────┬──────────────────────┤
│ Filters │ Ticket List │
│ │ │
│ New │ TK-... │
│ Mine │ Subject │
│ Shared │ Customer │
│ Waiting │ Status / Priority │
│ Closed │ │
└───────────────┴──────────────────────┘
94. Staff Ticket Detail
Desktop Layout:
┌──────────────┬──────────────────────┬───────────────┐
│ Ticket Info │ Conversation │ Customer Info │
│ │ │ │
│ Status │ Messages │ Mobile │
│ Priority │ Attachments │ Type │
│ Category │ Notes │ History │
│ Owner │ Reply │ Tickets │
│ │ │ │
└──────────────┴──────────────────────┴───────────────┘
در Mobile Responsive به ستونهای عمودی تبدیل شود.
95. Staff Ticket Tabs
حداقل:
جدید
انتخابشده برای من
تیکتهای من
Shared with me
در انتظار من
در انتظار مشتری
حلشده
بستهشده
برای Expertهای دیگر:
Ticketهایی که از Public Queue توسط فرد دیگری Lock شدهاند میتوانند در بخش جدا:
برداشتهشدهها
فقط با Title + Owner نمایش داده شوند.
96. Staff Search
Search حداقل روی:
Ticket Number
Subject
Customer Mobile
Customer Name
کار کند.
97. Filters
فیلتر:
Status
Category
Priority
Owner
Date
Unread
98. Ticket Read State
برای تشخیص Unread مدل:
TicketReadState
----------------
ticket
user
last_read_at
last_read_message
وجود داشته باشد.
99. Customer History
در Sidebar Staff نمایش داده شود:
نام
موبایل
نوع مشتری
تعداد Ticket
آخرین Ticketها
100. Privacy سابقه Customer
اگر Ticket قدیمی Private متعلق به Expert دیگری است:
Expert فعلی نباید الزاماً Full Content را ببیند.
Default:
Expert لیست کلی History را مشاهده کند، اما Full Content فقط بر اساس Ticket Permission.
Manager میتواند تمام History را مشاهده کند.
Permission اضافی:
tickets.view_customer_full_history
میتواند بعداً به Expert خاص داده شود.
101. SLA
SLA از ابتدا در Data Model لحاظ شود.
حتی اگر Dashboard SLA در فاز اول ساده باشد.
حداقل Metrics:
First Response Time
Resolution Time
Unanswered Tickets
Overdue Tickets
Tickets per Expert
Average Response Time
102. SLAConfig
Configuration قابل تغییر:
SLAConfig
-----------
priority
first_response_minutes
resolution_minutes
is_active
مثلاً Manager میتواند مقادیر را بعداً تعیین کند.
103. SLA Snapshot
وقتی Ticket ساخته میشود:
first_response_due_at
resolution_due_at
در Ticket ذخیره شود.
در نتیجه اگر SLA Config فردا تغییر کند، Deadline Ticket قبلی عوض نمیشود.
104. SLA V1
در V1 SLA بر اساس elapsed clock time محاسبه شود.
محاسبه ساعات اداری، تعطیلات و Calendar Business Hours خارج از Scope اولیه است و میتواند فاز بعد باشد.
105. First Response
first_response_at
زمان اولین PUBLIC Message از سمت Staff است.
Internal Note پاسخ محسوب نمیشود.
106. Security — XSS
متن Customer و Expert نباید Raw HTML Render شود.
مثلاً این:
<script>alert(1)</script>
نباید اجرا شود.
Django Autoescape باید فعال بماند.
Message content Plain Text باشد.
107. CSRF
تمام POST Requestها باید CSRF Protection داشته باشند.
HTMX نیز CSRF Token ارسال کند.
108. Session Security
Production:
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SameSite = Lax
TLS/HTTPS اجباری است.
109. Authorization Attachment
Download فایل باید همان Permission Ticket را بررسی کند.
کسی که Ticket را نمیتواند ببیند نباید فایل آن را هم Download کند.
110. Token Logging
Raw:
- OTP
- Magic Link Token
- Password
- API Token
نباید در Log ثبت شوند.
111. External API Secrets
SMS Token و Bale Token فقط از:
Environment Variables
یا Secret Management خوانده شوند.
داخل Git ذخیره نشوند.
112. File Upload Security
Nginx global upload limit باید کمی بیشتر از بزرگترین File Policy باشد.
Server application دوباره Policy دقیق هر File را بررسی کند.
113. HTML/JS Frontend Strategy
Frontend SPA نیست.
Routing Server-side Django است.
HTMX برای:
- Reply
- Status change
- Claim
- Transfer
- Share
- Filter
- Pagination
- Notes
- Edit
- Delete
- Counter refresh
استفاده شود.
114. Alpine.js
Alpine برای Stateهای کوچک Frontend:
Modal
Dropdown
Voice recorder state
File preview
Attachment queue
Tabs
Confirm dialogs
استفاده شود.
115. Vanilla JavaScript
برای APIهای Browser مانند:
MediaRecorder
File API
Clipboard
Drag & Drop
Vanilla JavaScript استفاده شود.
116. Tailwind CSS
UI کاملاً Responsive باشد.
RTL فارسی در Design System از ابتدا لحاظ شود.
HTML Root:
<html lang="fa" dir="rtl">
117. تاریخ
DateTime داخل Database استاندارد و timezone-aware ذخیره شود.
Backend بهتر است UTC نگهداری کند.
در UI بر اساس Timezone موردنظر شرکت نمایش داده شود.
Jalali صرفاً Presentation Layer است و نباید نوع Date Database را تغییر دهد.
118. Suggested URLs — Customer
/login/
/login/otp/
/logout/
/profile/
/tickets/
/tickets/new/
/tickets/<uuid>/
/tickets/<uuid>/reply/
/t/<magic-token>/
119. Suggested URLs — Staff
/staff/login/
/staff/logout/
/staff/
/staff/tickets/
/staff/tickets/<uuid>/
/staff/tickets/<uuid>/claim/
/staff/tickets/<uuid>/transfer/
/staff/tickets/<uuid>/share/
/staff/tickets/<uuid>/status/
/staff/tickets/<uuid>/priority/
/staff/tickets/<uuid>/reply/
/staff/tickets/<uuid>/note/
120. Message URLs
/messages/<uuid>/edit/
/messages/<uuid>/delete/
/messages/<uuid>/revisions/
Permission Server Side اجباری است.
121. Attachment URL
/files/<uuid>/download/
هیچ Raw media URL عمومی وجود نداشته باشد.
122. Django Admin
در V1 استفاده از Django Admin برای Configurationهای داخلی کاملاً قابل قبول است.
مثلاً:
- Categories
- File Policies
- SLA
- Groups
- Permissions
- Staff
- Customer types
نیازی نیست از روز اول برای همه تنظیمات Custom Admin Panel نوشته شود.
123. Database Constraints
حداقل موارد زیر Constraint داشته باشند:
mobile unique
ticket_number unique
public_id unique
one TicketReadState per ticket/user
one active TicketAccessGrant per ticket/user
124. Database Indexes
Index روی:
Ticket.status
Ticket.priority
Ticket.owner
Ticket.selected_expert
Ticket.customer
Ticket.category
Ticket.created_at
Ticket.last_activity_at
Message.ticket
Message.created_at
NotificationJob.status
NotificationJob.next_attempt_at
OTPChallenge.mobile
OTPChallenge.created_at
ایجاد شود.
125. Transactions
عملیات زیر Atomic باشند:
Claim Ticket
Transfer
Share/Revoke
Customer Reply + Reopen
Expert Reply + Status Update
Message Edit + Revision
Message Soft Delete
126. Service — create_ticket
ورودی:
customer
category
subject
selected_expert
body
attachments
عملیات:
- Validation
- Create Ticket
- Create Initial Message
- Save Attachments
- Create TicketEvent
- Update timestamps
- Queue Bale Notification
همه عملیات Database باید Transactional باشند.
127. Service — reply_ticket
بررسی:
can_reply()
سپس:
- Create Message
- Attach Files
- Update last activity
- Update waiting status
- Generate Notification Job
- Reopen اگر لازم است
128. Status بعد از Customer Reply
اگر Ticket:
OPEN
WAITING_CUSTOMER
باشد:
WAITING_STAFF
شود.
اگر:
RESOLVED
CLOSED
باشد:
REOPENED
شود.
129. Status بعد از Staff Reply
بعد از PUBLIC Staff reply:
WAITING_CUSTOMER
شود.
اگر اولین Staff reply است:
first_response_at
ثبت شود.
130. Claim Status
هنگام Claim یک Ticket NEW:
OPEN
شود.
Claim لزوماً Staff Reply محسوب نمیشود.
131. Resolved vs Closed
RESOLVED:
کارشناس معتقد است مسئله حل شده ولی Ticket هنوز منطقی قابل ادامه است.
CLOSED:
Ticket رسماً بسته شده است.
هر دو با Customer reply میتوانند Reopen شوند.
132. Internal Note Status
Internal Note نباید Ticket Status را به:
WAITING_CUSTOMER
تغییر دهد.
133. Mention Notification
Internal Note نیز میتواند Mention داشته باشد.
اگر Expert دسترسی دارد، Bale Notification ارسال شود.
134. Notification Failure
Failure SMS/Bale نباید باعث Rollback شدن Message شود.
External Notification Eventual Delivery است.
135. Retry
Notification Worker:
attempt 1
attempt 2
attempt 3
...
با Backoff انجام دهد.
تعداد Max Retry Configurable باشد.
Failure نهایی در Admin قابل مشاهده باشد.
136. Logging
Application Logging شامل:
request errors
provider errors
notification failures
permission denial
worker errors
file upload failures
باشد.
ولی Sensitive Data در Log ماسک شود.
137. Docker Architecture
Production Compose:
nginx
web
worker
db
ساختار:
Internet
↓
Nginx
↓
Gunicorn
↓
Django
↓
PostgreSQL
Worker:
PostgreSQL
↑
Django Notification Worker
↓
SMS / Bale APIs
138. Docker Volumes
postgres_data
media_data
static_data
Media Volume باید Persistent باشد.
Container Recreation نباید فایل Customer را حذف کند.
139. Redis
در V1:
Redis = NOT REQUIRED
دلایل:
- فقط 20 Expert
- حدود 200 Customer
- WebSocket نداریم
- Cache سنگین نداریم
- Notification Queue میتواند DB-backed باشد
140. زمان اضافهشدن Redis
Redis فقط در صورت نیاز واقعی اضافه شود، مثلاً:
WebSocket
High-volume queue
Heavy cache
Distributed rate limiting
Thousands of concurrent sessions
141. Celery
Celery نیز در V1 لازم نیست.
Database Notification Worker کافی است.
اگر Background Tasks بسیار زیاد شدند:
Redis + Celery
بدون تغییر Business Logic اصلی قابل اضافهشدن باشد.
142. Backup
چون فایلها Local هستند، Backup بخش حیاتی سیستم است.
Backup باید شامل:
PostgreSQL
Media Volume
Environment configuration backup
باشد.
143. PostgreSQL Backup
حداقل Automated:
pg_dump
به صورت منظم انجام شود.
144. Media Backup
media_data باید جدا Backup شود.
فقط Database Backup کافی نیست.
زیرا Attachmentها داخل DB ذخیره نمیشوند.
145. Restore Test
تیم DevOps باید Restore Database + Media را حداقل قبل از Production Release تست کند.
Backup بدون Restore Test قابل اعتماد نیست.
146. Development Environment
Development:
Docker Compose
PostgreSQL
Django runserver/Gunicorn optional
Tailwind watcher
SQLite فقط برای تست بسیار ساده محلی قابل استفاده است.
Environment اصلی Development و Production بهتر است PostgreSQL باشد تا اختلاف رفتار کاهش یابد.
147. Database نهایی
انتخاب قطعی:
PostgreSQL
SQLite برای Production این پروژه توصیه نمیشود.
148. API
در V1 نیازی به Django REST Framework نیست.
چون:
- Mobile App نداریم.
- SPA نداریم.
- Frontend Django Template است.
Viewهای Django + HTMX کافی هستند.
149. DRF Future
Service Layer باید طوری نوشته شود که در آینده اگر:
- Mobile App
- Public API
- CRM Integration
ایجاد شد، DRF بتواند همان Serviceها را استفاده کند.
150. Testing Strategy
سه سطح اصلی:
Unit Tests
Service Tests
Integration/View Tests
151. Permission Tests
مهمترین Tests پروژه Permission Tests هستند.
مثلاً باید ثابت شود:
Customer A هرگز Ticket Customer B را نمیبیند.
Expert A نباید Ticket Private Expert B را ببیند.
Expert نباید بعد از Lock توسط Expert دیگر محتوای Ticket را ببیند.
Manager باید تمام موارد را ببیند.
152. Claim Concurrency Test
Test همزمانی ضروری:
دو Expert همزمان Claim میکنند.
فقط یکی موفق شود.
153. Magic Link Tests
تست:
valid link
expired link
used link
revoked link
wrong ticket
wrong customer
invalid token
154. OTP Tests
تست:
valid OTP
expired OTP
wrong OTP
maximum attempts
resend cooldown
rate limit
155. Message Tests
تست:
edit
revision creation
soft delete
manager sees deleted content
customer sees tombstone
156. Attachment Tests
تست:
valid type
invalid type
size too large
multiple files
private download
unauthorized download
157. Ticket Status Tests
تمام Transitionها تست شوند.
خصوصاً:
CLOSED + customer reply → REOPENED
RESOLVED + customer reply → REOPENED
staff public reply → WAITING_CUSTOMER
customer reply → WAITING_STAFF
internal note → no waiting change
158. Notification Tests
External APIها Mock شوند.
Test شود:
SMS queued
Bale queued
retry
failure
deduplication
159. Coding Standards
پیشنهاد:
Ruff
Black-compatible formatting
Type hints for Services
pytest
pytest-django
Business-critical Functions Type Hint داشته باشند.
160. JavaScript Standards
JavaScript پیچیده Global نوشته نشود.
Componentها جدا:
voice-recorder.js
attachment-uploader.js
ticket-actions.js
mention-autocomplete.js
161. Template Structure
templates/
├── base.html
├── customer/
├── staff/
├── tickets/
│ └── partials/
└── components/
HTMX partialها از Page Templateها جدا باشند.
162. HTMX Conventions
HTMX Endpoint باید در صورت:
HX-Request
Partial برگرداند.
و در Request عادی Full Page یا Redirect مناسب بدهد.
163. Error UX
Errorها به فارسی قابل فهم باشند.
بد:
IntegrityError
خوب:
این تیکت لحظاتی قبل توسط کارشناس دیگری برداشته شده است.
164. Empty States
صفحات Empty State مناسب داشته باشند.
مثلاً:
هنوز تیکتی ثبت نکردهاید.
و CTA:
ثبت تیکت جدید
165. Loading States
برای:
- Upload
- Reply
- Claim
- Transfer
Loading/Disabled State وجود داشته باشد تا Double Submit رخ ندهد.
166. Responsive Design
Customer UI Mobile-first باشد.
چون Magic Link از SMS احتمالاً روی Mobile باز میشود.
Staff UI Desktop-first ولی Responsive باشد.
167. Accessibility
حداقل:
- Label برای Formها
- Keyboard navigation
- Button semantic
- Contrast مناسب
- Focus state
رعایت شود.
168. No Hard Delete Policy
در Business Data:
- Ticket
- Message
- Attachment
Hard Delete از UI نداشته باشیم.
در صورت نیاز Maintenance Admin جدا و بسیار محدود باشد.
169. Ticket Deletion
در V1 Customer و Expert نباید Ticket را Delete کنند.
Ticket میتواند Closed/Archived شود.
170. Archive
ARCHIVED فعلاً لازم نیست Status اصلی باشد.
در آینده میتواند به عنوان Flag:
is_archived
اضافه شود.
در V1 Closed کافی است.
171. Selected Expert Availability
فقط Staff فعال و Customer-selectable در انتخاب Customer نمایش داده شود.
اگر Expert بعداً غیرفعال شد:
Ticketهای قبلی حذف نمیشوند.
Manager میتواند آنها را Transfer کند.
172. Customer Disable
اگر Customer غیرفعال شود:
Login جدید نداشته باشد.
سوابق Ticket حذف نشوند.
173. Staff Disable
Staff غیرفعال:
- Login ندارد.
- در Expert Selection نمایش داده نمیشود.
- History باقی میماند.
Ticketهای Active او باید توسط Manager قابل Transfer باشند.
174. Ticket Creation Notification
اگر Customer Expert انتخاب کرده:
Bale فقط برای همان Expert.
اگر انتخاب نکرده:
Bale برای Expertهای فعال و مجاز Queue عمومی.
175. Customer SMS
SMS فقط برای Public Staff Reply ارسال شود.
این موارد SMS ایجاد نکنند:
Internal Note
Claim
Transfer
Priority change
Status internal changes
Mention
مگر در آینده Policy تغییر کند.
176. Message Composer Staff
Composer Staff حداقل Mode داشته باشد:
پاسخ به مشتری
یادداشت داخلی
رنگ و ظاهر دو Mode کاملاً متفاوت باشد تا Expert اشتباهاً Note داخلی را برای Customer ارسال نکند.
177. Internal Note Safety
هنگام INTERNAL_NOTE:
Button باید واضح باشد:
ثبت یادداشت داخلی
نه:
ارسال
178. Attachment در Internal Note
Internal Note میتواند Attachment داشته باشد.
Customer نباید به آن دسترسی پیدا کند.
حتی اگر URL فایل را حدس بزند، Authorization باید مانع شود.
179. Message Ordering
Messageها بر اساس:
created_at
id
ترتیب پایدار داشته باشند.
180. Pagination
اگر Ticket طولانی شد، Messageها نباید بدون محدودیت یکجا Load شوند.
در V1 میتوان آخرین مثلاً 50 Message را Load کرد و:
نمایش پیامهای قبلی
داشت.
181. Ticket Philosophy
با وجود Conversation UI داخل Ticket، Customer باید برای موضوع متفاوت Ticket جدید ثبت کند.
در UI میتوان Text راهنما نمایش داد:
اگر سؤال شما مربوط به موضوع دیگری است، لطفاً تیکت جدید ثبت کنید.
182. Initial Page Performance
Dashboard Staff نباید N+1 Query داشته باشد.
استفاده مناسب از:
select_related()
prefetch_related()
ضروری است.
183. Query Count
Ticket List نباید برای هر Ticket Query جدا برای:
- Customer
- Category
- Owner
اجرا کند.
184. Database JSON
JSONField فقط برای Metadata متغیر مثل:
TicketEvent.metadata
NotificationJob payload
استفاده شود.
Core Domain Data داخل JSON ذخیره نشود.
185. System Configuration
تنظیمات Runtime که مدیر ممکن است تغییر دهد بهتر است DB-driven باشند.
مثلاً:
AttachmentPolicy
SLAConfig
TicketCategory
Secrets و Infrastructure settings در Environment بمانند.
186. Out of Scope V1
موارد زیر در V1 اجرا نشوند مگر Requirement جدید ایجاد شود:
WebSocket
Live typing
Online status
Reaction
Video call
Voice call
Mobile app
Multi-company
Team hierarchy
Product catalog
ERP integration
Advanced BI
Elasticsearch
AI chatbot
S3
Object Storage
Kafka
Microservices
187. Recommended Development Phases
Phase 1 — Foundation
- Django project
- Custom User
- PostgreSQL
- Docker
- Authentication
- Staff login
- Customer OTP
- Basic permissions
Phase 2 — Ticket Core
- Category
- Ticket
- Status
- Priority
- Customer selection Expert
- Public Queue
- Claim
- Ticket detail
Phase 3 — Messaging
- Reply
- Edit
- Revision
- Soft delete
- Internal Note
- Mentions
Phase 4 — Permission
- Private selected Expert
- Locked Ticket
- TicketAccessGrant
- Share
- Revoke
- Transfer
- Manager full access
Phase 5 — Attachments
- Images
- Word
- Excel
- ZIP/RAR
- Video
- Multiple Attachments
- Private downloads
Phase 6 — Voice
- MediaRecorder
- Preview
- Timer
- Upload
Phase 7 — Notifications
- SMS Provider abstraction
- Bale Provider
- Database Outbox
- Worker
- Retry
Phase 8 — Magic Link
- Secure token
- SMS link
- Session bootstrap
- 7-day expiry
- OTP fallback
Phase 9 — Dashboard & SLA
- Filters
- Search
- Unread
- SLA metrics
- Customer history
Phase 10 — Production Hardening
- Tests
- Permission tests
- Upload security
- Nginx protected media
- Logging
- Backup
- Docker production
- Security review
188. Definition of Done
یک Feature فقط زمانی Done محسوب شود که:
- Business Logic تکمیل باشد.
- Permission Server-side داشته باشد.
- Validation داشته باشد.
- Error UX مناسب داشته باشد.
- Test داشته باشد.
- Responsive باشد.
- Security آن بررسی شده باشد.
- Logging مناسب داشته باشد.
- در Docker Production قابل اجرا باشد.
189. Acceptance Criteria — Customer Login
- Customer شماره موبایل وارد میکند.
- OTP دریافت میکند.
- OTP صحیح Login ایجاد میکند.
- OTP اشتباه محدود میشود.
- OTP منقضی Reject میشود.
- Customer بدون Password قابل استفاده است.
- Customer میتواند بعداً Password تعیین کند.
190. Acceptance Criteria — New Ticket
Customer باید بتواند:
- Category انتخاب کند.
- Subject وارد کند.
- Expert انتخاب کند یا انتخاب نکند.
- Message بنویسد.
- چند Attachment اضافه کند.
- Ticket ثبت کند.
Ticket در Dashboard صحیح Expertها ظاهر شود.
191. Acceptance Criteria — Selected Expert
اگر Customer احمد را انتخاب کند:
- احمد Full Ticket را میبیند.
- رضا Ticket را نمیبیند.
- Manager Ticket را میبیند.
192. Acceptance Criteria — Public Ticket
اگر Customer Expert انتخاب نکند:
- همه Expertهای مجاز Ticket را میبینند.
- همه تا قبل از Claim میتوانند پاسخ دهند.
193. Acceptance Criteria — Claim
وقتی احمد Ticket را Claim کرد:
- احمد Full Access دارد.
- رضا فقط Title + Owner را میبیند.
- رضا Content را نمیبیند.
- رضا Reply نمیتواند بکند.
- Manager Full Access دارد.
194. Acceptance Criteria — Shared Access
احمد میتواند رضا را Share کند.
بعد از Share:
- رضا Full Content را میبیند.
- رضا Reply میکند.
- Access قابل Revoke است.
195. Acceptance Criteria — Transfer
احمد Ticket را به رضا Transfer میکند.
- Owner = رضا
- تاریخچه Transfer ثبت میشود.
- نیاز به Manager Approval نیست.
- دسترسی احمد به صورت Default حذف میشود.
196. Acceptance Criteria — Edit
Expert/Customer Message خود را Edit میکند.
- متن جدید نمایش داده میشود.
editedنمایش داده میشود.- متن قبلی در Revision باقی میماند.
- Manager مجاز Revision را میبیند.
197. Acceptance Criteria — Delete
Expert Message خودش را Delete میکند.
Customer:
این پیام توسط فرستنده حذف شده است.
Manager:
- متن اصلی
- Attachment
- زمان حذف
را میبیند.
198. Acceptance Criteria — Voice
در Browser:
- Record شروع شود.
- Timer کار کند.
- Stop کار کند.
- Preview کار کند.
- Delete/Re-record کار کند.
- Voice ارسال شود.
- در Ticket قابل پخش باشد.
199. Acceptance Criteria — Magic Link
Expert پاسخ میدهد.
Customer SMS دریافت میکند.
Customer Link را باز میکند.
بدون OTP:
- Ticket باز میشود.
- Message دیده میشود.
- Reply ممکن است.
Token بعد از مصرف از URL حذف شود.
Link منقضی Customer را به OTP هدایت کند.
200. Acceptance Criteria — Closed/Reopen
Expert Ticket را Close میکند.
Customer دوباره Reply میکند.
سیستم:
CLOSED → REOPENED
کند.
Expert مربوطه Notification دریافت کند.
201. Acceptance Criteria — Internal Note
Expert Note ثبت میکند.
- Staff مجاز آن را میبینند.
- Customer آن را نمیبیند.
- SMS Customer ارسال نمیشود.
- Note ظاهری متفاوت دارد.
202. Acceptance Criteria — Mention
Expert مجاز:
@reza
را Mention میکند.
اگر Reza Access دارد:
- Mention ذخیره شود.
- Bale Notification ارسال شود.
اگر Access ندارد:
سیستم نباید اطلاعات Ticket را Leak کند و باید Share Flow انجام شود.
203. Performance Target
برای Scale فعلی:
صفحات عادی باید در شبکه Local/Server مناسب معمولاً بدون Query/Processing غیرضروری پاسخ سریع داشته باشند.
Performance مهمتر از معماری پیچیده است.
Caching در V1 ضرورتی ندارد.
204. Final Technology Decision
Stack قطعی:
Python
Django
PostgreSQL
Django Templates
HTMX
Alpine.js
Vanilla JavaScript
Tailwind CSS
Gunicorn
Nginx
Docker
Docker Compose
Local Protected Media Storage
Notification:
SMS Adapter
Bale Adapter
Database Outbox Worker
205. Technology Not Required
React NO
Vue NO
Next.js NO
Django REST NOT REQUIRED V1
Redis NO
Celery NO
WebSocket NO
Django Channels NO
S3 NO
MinIO NO
Kafka NO
Microservices NO
Elasticsearch NO
206. مهمترین قوانین Business
این قواعد باید به عنوان قوانین غیرقابل نقض پروژه در نظر گرفته شوند:
Rule 1
Manager دارای Permission کامل همیشه تمام Ticketها را میبیند.
Rule 2
Customer فقط Ticketهای خودش را میبیند.
Rule 3
اگر Customer Expert انتخاب کند، Ticket برای سایر Expertها Private است.
Rule 4
Ticket بدون Expert ابتدا Public Queue است.
Rule 5
بعد از Claim، Ticket Private میشود.
Rule 6
بعد از Claim، Expertهای دیگر فقط Title و Owner را میبینند.
Rule 7
Owner یا Manager میتواند Expert دیگری را Share کند.
Rule 8
Transfer نیاز به Manager Approval ندارد.
Rule 9
Message Edit باید Revision History داشته باشد.
Rule 10
Expert Message Delete همیشه Soft Delete است.
Rule 11
مدیر مجاز Original Deleted Message را میبیند.
Rule 12
Internal Note هرگز به Customer نمایش داده نمیشود.
Rule 13
Attachment Permission وابسته به Ticket Permission است.
Rule 14
Customer Reply روی Closed/Resolved باعث Reopen میشود.
Rule 15
Priority را Customer تعیین نمیکند.
Rule 16
Notification failure نباید Message را Rollback کند.
Rule 17
Magic Link باید Secure، Expiring و Revocable باشد.
Rule 18
هیچ فایل Ticket به صورت Public Media URL سرو نشود.
Rule 19
هیچ Role Name داخل Business Logic Hard-Code نشود.
Rule 20
هر Permission حساس باید در Backend enforce شود.
207. معیار معماری
هدف این پروژه ساخت «پیچیدهترین معماری ممکن» نیست.
هدف:
Simple
Secure
Maintainable
Testable
Modular
Production-ready
است.
هر تکنولوژی جدید باید فقط زمانی اضافه شود که یک مشکل واقعی را حل کند.
برای Scale فعلی، Django + PostgreSQL به تنهایی بیش از اندازه کافی است.
208. نتیجه نهایی معماری
معماری نهایی:
┌──────────────┐
│ Customer │
└──────┬───────┘
│
OTP / Magic Link
│
▼
┌──────────────┐ ┌───────────────┐
│ Sales Expert │────────▶│ Nginx │
└──────────────┘ └───────┬───────┘
│
▼
┌───────────────┐
│ Django │
│ Modular Mono │
└───────┬───────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌──────────────┐
│ PostgreSQL │ │Local Media │ │Notification │
└────────────┘ └────────────┘ │ Outbox │
└──────┬───────┘
│
▼
┌─────────────────┐
│ Django Worker │
└────────┬────────┘
│
┌─────────────┴────────────┐
▼ ▼
SMS API Bale API
این معماری برای نیاز فعلی پروژه ساده، امن، قابل توسعه و کاملاً مناسب است.