Files
customerTicket/Software Requirements & Architecture Specification — سیستم تیکتینگ فروش.md
2026-09-10 11:34:19 +03:30

4029 lines
66 KiB
Markdown

# مشخصات کامل فنی و محصول
## سیستم تیکتینگ مشتریان و کارشناسان فروش
**نسخه سند:** 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. اهداف اصلی
سیستم باید:
1. ورود مشتری با شماره موبایل و OTP را فراهم کند.
2. ثبت‌نام اجباری سنتی نداشته باشد.
3. تعیین نام، نام خانوادگی و Password اختیاری باشد.
4. مشتری بتواند چند Ticket همزمان داشته باشد.
5. مشتری بتواند کارشناس موردنظر خود را انتخاب کند.
6. در صورت عدم انتخاب کارشناس، Ticket وارد صف عمومی کارشناسان شود.
7. کارشناس بتواند Ticket عمومی را برای خودش بردارد.
8. بعد از برداشتن Ticket، اطلاعات آن از سایر کارشناسان مخفی شود.
9. مدیران مجاز همیشه بتوانند همه Ticketها را مشاهده کنند.
10. امکان پاسخ متنی و Attachmentهای مختلف وجود داشته باشد.
11. Voice مستقیماً داخل مرورگر ضبط شود.
12. امکان Edit پیام وجود داشته باشد.
13. تاریخچه Edit کاملاً حفظ شود.
14. حذف پیام کارشناس به صورت Soft Delete باشد.
15. مدیر بتواند متن اصلی پیام حذف‌شده را مشاهده کند.
16. امکان Internal Note وجود داشته باشد.
17. امکان Transfer Ticket وجود داشته باشد.
18. امکان Share کردن Ticket با Expert دیگر وجود داشته باشد.
19. امکان Mention کارشناس وجود داشته باشد.
20. Notification مشتری از طریق SMS انجام شود.
21. Notification کارشناسان از طریق Dashboard و Bale انجام شود.
22. مشتری از طریق Magic Link بتواند سریع Ticket را باز کند.
23. Permission System منعطف باشد.
24. Priority و SLA قابل استفاده باشند.
25. فایل‌ها روی Storage محلی Server ذخیره شوند.
---
# 3. محدوده کاربران
حداکثر بار مورد انتظار در نسخه فعلی:
- حدود 200 مشتری
- حداکثر 20 کارشناس فروش
- یک شرکت
- بدون Multi-Tenancy
- حجم پیام و Ticket نسبتاً پایین
در نتیجه نیازی به معماری پیچیده Distributed وجود ندارد.
---
# 4. معماری کلان
معماری سیستم باید:
**Modular Monolith**
باشد.
تمام Business Logic داخل یک Django Project قرار می‌گیرد ولی Domainها به Appهای منطقی جدا تقسیم می‌شوند.
ساختار پیشنهادی:
```text
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:
```text
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` بسیار هزینه‌بر است.
مدل مفهومی:
```text
User
---------
id
public_id
username
mobile
email
password
account_type
is_active
is_staff
created_at
updated_at
last_login
```
`account_type`:
```text
CUSTOMER
STAFF
```
کاربر Customer می‌تواند Password نداشته باشد.
در این حالت:
```python
user.set_unusable_password()
```
استفاده شود.
---
# 8. CustomerProfile
```text
CustomerProfile
----------------
user
first_name
last_name
customer_type
created_at
updated_at
```
`customer_type` اختیاری است.
مقادیر اولیه:
```text
PERSON
CONSUMER
SHOP
REPRESENTATIVE
COMPANY
OTHER
```
نمایش فارسی:
```text
شخص
مصرف‌کننده نهایی
فروشگاه
نمایندگی
شرکت
سایر
```
---
# 9. StaffProfile
```text
StaffProfile
-------------
user
display_name
avatar
mobile
bale_chat_id
bale_notifications_enabled
is_customer_selectable
sort_order
is_active
created_at
updated_at
```
فقط کارشناسانی که:
```text
is_customer_selectable = True
```
دارند در صفحه انتخاب کارشناس به مشتری نمایش داده شوند.
---
# 10. احراز هویت مشتری
روش اصلی:
```text
Mobile
↓
OTP
↓
Login
```
Password اجباری نیست.
پس از ورود موفق، سیستم می‌تواند پیشنهاد دهد:
> برای ورود آسان‌تر در آینده می‌توانید رمز عبور تعیین کنید.
ولی این درخواست نباید Blocking باشد.
---
# 11. ورود با Password
در صورت تعیین Password، مشتری می‌تواند در آینده با:
```text
mobile + password
```
هم وارد شود.
با این حال روش OTP همچنان باید در صفحه Login در اولویت UX باشد.
---
# 12. Staff Login
کارشناسان و مدیران صفحه Login مستقل داشته باشند.
مثلاً:
```text
/staff/login/
```
Customer login:
```text
/login/
```
Staff Login با Username/Password انجام شود.
---
# 13. OTP Architecture
سیستم SMS نباید وابسته به Provider خاص باشد.
از Adapter Pattern و Protocol استفاده شود.
Interface مفهومی:
```python
class SMSProvider(Protocol):
def send_otp(
self,
mobile: str,
code: str,
) -> SMSResult:
...
def send_message(
self,
mobile: str,
message: str,
) -> SMSResult:
...
```
Providerهای آینده می‌توانند شامل:
```text
KavenegarProvider
SMSIrProvider
IPPanelProvider
FarazSMSProvider
CustomSMSProvider
```
باشند.
Business Logic حق ندارد مستقیم API Provider را Call کند.
---
# 14. OTPChallenge Model
```text
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
مقادیر اولیه پیشنهادی:
```text
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 شوند.
برای مثال:
```text
09121234567
```
به شکل Canonical:
```text
+989121234567
```
ذخیره شود.
تمام جستجو و Unique Constraint بر اساس شماره Normalize‌شده باشد.
---
# 17. TicketCategory
Category در Database ذخیره شود.
```text
TicketCategory
---------------
id
title
slug
description
icon
sort_order
is_active
created_at
updated_at
```
مثال:
```text
استعلام قیمت
درصد تخفیف
شرایط فروش
موجودی
شرایط ارسال
درخواست نمایندگی
شکایت
مشاوره
سایر
```
انتخاب Category برای Customer اجباری است.
Category غیرفعال برای Ticketهای قدیمی باقی بماند ولی در Ticket جدید نمایش داده نشود.
---
# 18. Ticket Model
مدل اصلی:
```text
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 باید شماره قابل خواندن داشته باشد.
مثلاً:
```text
TK-1405-001254
```
اما Permission و URL نباید بر اساس این شماره امنیت ایجاد کنند.
URLها از `public_id` یا UUID استفاده کنند.
مثلاً:
```text
/tickets/9d7b.../
```
---
# 20. Subject
Subject اجباری است.
محدوده پیشنهادی:
```text
5 تا 200 character
```
نمونه:
> استعلام قیمت لوله 110
---
# 21. اولین پیام Ticket
Ticket علاوه بر Subject باید یک Initial Message داشته باشد.
Initial Message می‌تواند شامل:
- Text
- Attachment
- Text + Attachments
باشد.
UX باید مشتری را تشویق کند مسئله را کامل توضیح دهد.
---
# 22. Ticket Status
Statusهای اصلی:
```text
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:
```text
RESOLVED
```
یا:
```text
CLOSED
```
باشد و Customer پیام جدید ارسال کند:
Ticket خودکار به:
```text
REOPENED
```
تغییر وضعیت دهد.
این عملیات باید Transactional باشد.
---
# 25. Priority
Priorityهای اولیه:
```text
NORMAL
IMPORTANT
URGENT
```
Default:
```text
NORMAL
```
Customer اجازه تغییر Priority ندارد.
کارشناس یا کاربران دارای Permission می‌توانند Priority را تغییر دهند.
---
# 26. منطق انتخاب Expert توسط Customer
سه حالت مهم وجود دارد.
## حالت اول: Customer Expert انتخاب می‌کند
مثلاً Customer:
```text
احمد
```
را انتخاب می‌کند.
در این حالت:
```text
selected_expert = Ahmad
owner = NULL
```
و فقط:
- Ahmad
- مدیران دارای دسترسی کامل
می‌توانند Ticket را مشاهده کنند.
سایر کارشناسان نباید حتی محتوای Ticket را ببینند.
این Ticket وارد Public Queue نمی‌شود.
---
# 27. عدم انتخاب Expert
اگر Customer کارشناسی انتخاب نکند:
```text
selected_expert = NULL
owner = NULL
```
Ticket وارد Public Queue می‌شود.
تمام کارشناسان مجاز می‌توانند:
- عنوان
- محتوا
- Attachment
- Customer
- سابقه مجاز
را مشاهده کنند.
---
# 28. Claim / Lock Ticket
در Public Queue هر Expert می‌تواند:
> برداشتن تیکت
یا:
> اختصاص به من
را انتخاب کند.
پس:
```text
owner = current_user
```
خواهد شد.
---
# 29. Race Condition در Claim
ممکن است دو Expert همزمان روی:
> اختصاص به من
کلیک کنند.
Claim باید با:
```python
transaction.atomic()
```
و:
```python
select_for_update()
```
پیاده‌سازی شود.
فقط اولین Expert موفق شود.
Expert دوم پیام مناسب دریافت کند:
> این تیکت لحظاتی قبل توسط «احمد رضایی» برداشته شد.
---
# 30. رفتار Ticket بعد از Claim
بعد از Claim:
فقط:
- Owner
- کارشناسان Shared
- مدیران دارای Permission
حق مشاهده محتوای Ticket را دارند.
سایر کارشناسان فقط اطلاعات محدود زیر را ببینند:
```text
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های دیگر مدل جداگانه ایجاد شود.
```text
TicketAccessGrant
------------------
id
ticket
user
granted_by
permission_level
created_at
revoked_at
```
در V1 می‌توان `permission_level` را ساده نگه داشت:
```text
FULL
```
ولی فیلد از ابتدا وجود داشته باشد تا بعداً:
```text
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 کند.
مثلاً:
```text
Ahmad → Reza
```
پس:
```text
owner = Reza
```
شود.
Transfer باید Transactional باشد.
---
# 36. دسترسی Owner قبلی بعد از Transfer
Default Behavior:
پس از Transfer، Owner قبلی دسترسی اختصاصی خود را از دست می‌دهد.
در صورت نیاز باید به عنوان Shared Expert به Ticket اضافه شود.
این رفتار از نشت ناخواسته اطلاعات جلوگیری می‌کند.
---
# 37. TicketEvent
Audit Log عمومی در V1 لازم نیست.
اما Domain History لازم است.
مدل:
```text
TicketEvent
------------
id
ticket
event_type
actor
metadata
created_at
```
Eventها:
```text
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 شوند.
یعنی کد نباید بگوید:
```python
if user.role == "manager":
```
بلکه:
```python
user.has_perm(...)
```
یا Ticket Policy Service استفاده شود.
---
# 39. Django Groups
برای ساده نگه‌داشتن معماری از:
- Django Groups
- Django Permissions
استفاده شود.
مثلاً Groupهای اولیه:
```text
Sales Expert
Sales Manager
Administrator
```
نام Group Business Logic نیست.
Permission تعیین‌کننده رفتار است.
---
# 40. Permissions پیشنهادی
```text
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. مدیران
مدیرانی که:
```text
tickets.view_all_tickets
```
دارند باید بدون استثناء تمام Ticketها را مشاهده کنند.
فرقی نمی‌کند:
- Customer Expert انتخاب کرده باشد.
- Ticket Locked شده باشد.
- Ticket Shared نشده باشد.
- Ticket متعلق به Expert دیگری باشد.
---
# 42. TicketPolicyService
Permission Ticket باید در یک نقطه مرکزی باشد.
مثلاً:
```python
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
```text
Message
---------------
id
public_id
ticket
author
body
message_type
is_internal
created_at
updated_at
edited_at
deleted_at
deleted_by
```
---
# 44. انواع Message
```text
PUBLIC
INTERNAL_NOTE
```
Customer فقط PUBLIC را مشاهده می‌کند.
---
# 45. Internal Note
Expert بتواند Note داخلی ایجاد کند.
مثال:
> این مشتری تخفیف ویژه درخواست کرده، قبل از اعلام قیمت با مدیر هماهنگ شود.
Internal Note:
- در Timeline کارشناسان نمایش داده شود.
- از پیام‌های معمولی کاملاً متمایز باشد.
- Customer هرگز آن را نبیند.
- در SMS Customer اثر نداشته باشد.
---
# 46. Edit Message
Customer و Expert بتوانند Message خودشان را Edit کنند.
بعد از Edit:
```text
edited_at
```
ثبت شود.
در UI:
> ویرایش شده
نمایش داده شود.
---
# 47. MessageRevision
قبل از تغییر متن، نسخه قبلی ذخیره شود.
```text
MessageRevision
----------------
id
message
body
edited_by
created_at
```
مثلاً:
نسخه 1:
```text
قیمت 1,200,000 تومان
```
نسخه جدید:
```text
قیمت 1,250,000 تومان
```
هر دو باید قابل بازیابی برای Manager مجاز باشند.
---
# 48. مشاهده Revision
Customer الزاماً Revision History را نمی‌بیند.
Manager یا فرد دارای:
```text
messaging.view_message_revisions
```
می‌تواند تاریخچه را مشاهده کند.
---
# 49. Delete Message
Expert می‌تواند Message خودش را Delete کند.
اما:
**هیچ Hard Delete انجام نشود.**
ممنوع:
```python
message.delete()
```
به جای آن:
```text
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 روی متن باشد.
مدل:
```text
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 دریافت کند.
مثال:
```text
تیکت TK-1405-1254
علی رضایی شما را Mention کرد:
«لطفاً شرایط تخفیف این مشتری را بررسی کنید.»
مشاهده تیکت:
https://support.example.com/...
```
---
# 55. Attachment Model
```text
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
سیستم باید حداقل پشتیبانی کند:
```text
IMAGE
VIDEO
VOICE
PDF
WORD
EXCEL
ZIP
RAR
OTHER
```
---
# 57. AttachmentPolicy
محدودیت‌ها نباید Hard-Code باشند.
مدل:
```text
AttachmentPolicy
-----------------
attachment_type
is_enabled
max_file_size
max_files_per_message
allowed_extensions
allowed_mime_types
sort_order
updated_at
```
---
# 58. محدودیت اولیه پیشنهادی
این اعداد فقط Default هستند و باید قابل تغییر باشند.
```text
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 داشته باشد.
مثلاً:
```text
متن
+ 4 تصویر
+ یک PDF
+ یک Voice
```
مجاز باشد.
---
# 60. Attachment Composer UI
قبل از ارسال، UI باید نشان دهد:
- Thumbnail تصویر
- نام فایل
- Size
- Type
- Progress
- حذف قبل از ارسال
- Error فایل نامعتبر
---
# 61. File Validation
صرفاً Extension کافی نیست.
Server باید بررسی کند:
1. Extension
2. MIME Type
3. File Signature
برای File Signature استفاده از `libmagic` یا ابزار مشابه توصیه می‌شود.
---
# 62. ZIP و RAR
ZIP/RAR نباید روی Server Extract شوند.
فقط به عنوان Attachment ذخیره شوند.
این موضوع برای جلوگیری از:
- path traversal
- zip bombs
- فایل‌های اجرایی داخلی
مهم است.
---
# 63. File Name Security
نام واقعی فایل کاربر نباید مستقیماً نام فایل Storage باشد.
مثلاً:
```text
invoice.pdf
```
روی Disk به:
```text
1b3ef72c-....pdf
```
تبدیل شود.
`original_name` فقط Metadata است.
---
# 64. Media Storage
Storage کاملاً Local Server است.
ساختار پیشنهادی:
```text
/media/
└── tickets/
└── 2026/
└── 09/
└── <ticket_uuid>/
└── <message_uuid>/
├── file_uuid.jpg
├── file_uuid.pdf
└── file_uuid.webm
```
---
# 65. Private Media
فایل‌های Ticket نباید مستقیماً از Public `/media/` قابل دسترسی باشند.
این URL ناامن است:
```text
https://site.com/media/tickets/xxx.pdf
```
چون Permission را دور می‌زند.
Download باید ابتدا Django Authorization را بررسی کند.
---
# 66. Nginx X-Accel-Redirect
روش پیشنهادی:
1. درخواست Download وارد Django می‌شود.
2. Django Permission را بررسی می‌کند.
3. در صورت مجاز بودن Header مخصوص Nginx برگردانده می‌شود.
4. Nginx فایل را Efficient ارسال می‌کند.
Location فایل‌ها در Nginx باید:
```text
internal
```
باشد.
---
# 67. Voice Recording
Voice با:
```text
MediaDevices.getUserMedia()
MediaRecorder
```
پیاده‌سازی شود.
نیازی به Plugin خارجی نیست.
---
# 68. Voice UI
Voice Recorder شامل:
```text
Record
Stop
Pause
Resume
Timer
Preview
Delete
Re-record
Send
```
باشد.
حداقل امکانات:
```text
🎤 ضبط
⏱ مدت ضبط
▶ پخش
🗑 حذف
➤ ارسال
```
---
# 69. Browser Compatibility Voice
Format ضبط ممکن است بین Browserها متفاوت باشد.
مثلاً:
```text
audio/webm
audio/ogg
audio/mp4
```
Frontend باید با:
```javascript
MediaRecorder.isTypeSupported()
```
Format مناسب را انتخاب کند.
Server نباید فقط یک MIME Type را قبول کند.
---
# 70. Video
در V1 نیازی به Transcoding Video نیست.
Video همان Format مجاز Upload می‌شود.
در Browser در صورت پشتیبانی:
```html
<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 داشته باشد.
مثلاً:
```text
پاسخ جدیدی برای درخواست شما ثبت شد.
مشاهده:
https://support.example.com/t/AbCDef...
```
---
# 74. Magic Link Security
Token باید حداقل:
```text
256-bit secure random
```
باشد.
برای تولید:
```python
secrets.token_urlsafe()
```
یا روش معادل Cryptographically Secure استفاده شود.
---
# 75. TicketMagicLink Model
```text
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 تا:
```text
7 روز
```
معتبر است.
---
# 77. Single-use Magic Link
برای امنیت بهتر، Magic Link باید نقش Bootstrap Login داشته باشد.
Flow:
```text
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:
```text
Expired Magic Link
↓
OTP Login
↓
Successful Login
↓
Original Ticket
```
---
# 80. Referrer Security
صفحه Consume Magic Link باید Header مناسب داشته باشد:
```text
Referrer-Policy: no-referrer
```
تا Token به سایت‌های خارجی Leak نشود.
---
# 81. SMS Notification Policy
پیشنهاد Default:
بعد از اولین پاسخ عمومی Expert پس از آخرین پیام Customer، SMS ارسال شود.
اگر Expert پشت سر هم 3 پیام ارسال کرد:
سه SMS جداگانه ارسال نشود.
مثلاً:
```text
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 نیست.
مدل:
```text
NotificationJob
----------------
id
channel
event_type
customer
staff_user
ticket
status
attempts
next_attempt_at
last_error
dedupe_key
created_at
sent_at
```
Channel:
```text
SMS
BALE
```
Status:
```text
PENDING
PROCESSING
SENT
FAILED
```
---
# 83. Notification Worker بدون Redis
در Docker Compose یک Process سبک اجرا شود:
```text
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 مفهومی:
```python
class StaffNotifier(Protocol):
def send_message(
self,
chat_id: str,
text: str,
) -> NotificationResult:
...
```
پیاده‌سازی:
```text
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:
```text
تیکت‌های من
```
هر Card:
```text
Ticket Number
Subject
Category
Status
Last Update
Selected/Assigned Expert
```
را نمایش دهد.
---
# 90. New Ticket Page
Form:
```text
Category *
Subject *
Expert انتخابی
Description / Message
Attachments
```
Expert Selection با Card:
```text
Avatar
Name
```
و گزینه:
> انتخاب کارشناس خاصی ندارم
---
# 91. Ticket Detail مشتری
صفحه شامل:
```text
Ticket header
Status
Category
Expert
Messages
Attachments
Reply form
Voice Recorder
```
باشد.
Internal Notes هرگز در HTML Customer Rendering نشوند.
---
# 92. Customer Profile
صفحه ساده:
```text
نام
نام خانوادگی
نوع مشتری
شماره موبایل
Password
```
Mobile تغییر مستقیم نداشته باشد مگر با OTP Verification جداگانه.
---
# 93. Staff Dashboard
Layout پیشنهادی Desktop:
```text
┌──────────────────────────────────────┐
│ Header / Search / Notifications │
├───────────────┬──────────────────────┤
│ Filters │ Ticket List │
│ │ │
│ New │ TK-... │
│ Mine │ Subject │
│ Shared │ Customer │
│ Waiting │ Status / Priority │
│ Closed │ │
└───────────────┴──────────────────────┘
```
---
# 94. Staff Ticket Detail
Desktop Layout:
```text
┌──────────────┬──────────────────────┬───────────────┐
│ Ticket Info │ Conversation │ Customer Info │
│ │ │ │
│ Status │ Messages │ Mobile │
│ Priority │ Attachments │ Type │
│ Category │ Notes │ History │
│ Owner │ Reply │ Tickets │
│ │ │ │
└──────────────┴──────────────────────┴───────────────┘
```
در Mobile Responsive به ستون‌های عمودی تبدیل شود.
---
# 95. Staff Ticket Tabs
حداقل:
```text
جدید
انتخاب‌شده برای من
تیکت‌های من
Shared with me
در انتظار من
در انتظار مشتری
حل‌شده
بسته‌شده
```
برای Expertهای دیگر:
Ticketهایی که از Public Queue توسط فرد دیگری Lock شده‌اند می‌توانند در بخش جدا:
> برداشته‌شده‌ها
فقط با Title + Owner نمایش داده شوند.
---
# 96. Staff Search
Search حداقل روی:
```text
Ticket Number
Subject
Customer Mobile
Customer Name
```
کار کند.
---
# 97. Filters
فیلتر:
```text
Status
Category
Priority
Owner
Date
Unread
```
---
# 98. Ticket Read State
برای تشخیص Unread مدل:
```text
TicketReadState
----------------
ticket
user
last_read_at
last_read_message
```
وجود داشته باشد.
---
# 99. Customer History
در Sidebar Staff نمایش داده شود:
```text
نام
موبایل
نوع مشتری
تعداد Ticket
آخرین Ticketها
```
---
# 100. Privacy سابقه Customer
اگر Ticket قدیمی Private متعلق به Expert دیگری است:
Expert فعلی نباید الزاماً Full Content را ببیند.
Default:
Expert لیست کلی History را مشاهده کند، اما Full Content فقط بر اساس Ticket Permission.
Manager می‌تواند تمام History را مشاهده کند.
Permission اضافی:
```text
tickets.view_customer_full_history
```
می‌تواند بعداً به Expert خاص داده شود.
---
# 101. SLA
SLA از ابتدا در Data Model لحاظ شود.
حتی اگر Dashboard SLA در فاز اول ساده باشد.
حداقل Metrics:
```text
First Response Time
Resolution Time
Unanswered Tickets
Overdue Tickets
Tickets per Expert
Average Response Time
```
---
# 102. SLAConfig
Configuration قابل تغییر:
```text
SLAConfig
-----------
priority
first_response_minutes
resolution_minutes
is_active
```
مثلاً Manager می‌تواند مقادیر را بعداً تعیین کند.
---
# 103. SLA Snapshot
وقتی Ticket ساخته می‌شود:
```text
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 شود.
مثلاً این:
```html
<script>alert(1)</script>
```
نباید اجرا شود.
Django Autoescape باید فعال بماند.
Message content Plain Text باشد.
---
# 107. CSRF
تمام POST Requestها باید CSRF Protection داشته باشند.
HTMX نیز CSRF Token ارسال کند.
---
# 108. Session Security
Production:
```text
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 فقط از:
```text
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:
```text
Modal
Dropdown
Voice recorder state
File preview
Attachment queue
Tabs
Confirm dialogs
```
استفاده شود.
---
# 115. Vanilla JavaScript
برای APIهای Browser مانند:
```text
MediaRecorder
File API
Clipboard
Drag & Drop
```
Vanilla JavaScript استفاده شود.
---
# 116. Tailwind CSS
UI کاملاً Responsive باشد.
RTL فارسی در Design System از ابتدا لحاظ شود.
HTML Root:
```html
<html lang="fa" dir="rtl">
```
---
# 117. تاریخ
DateTime داخل Database استاندارد و timezone-aware ذخیره شود.
Backend بهتر است UTC نگهداری کند.
در UI بر اساس Timezone موردنظر شرکت نمایش داده شود.
Jalali صرفاً Presentation Layer است و نباید نوع Date Database را تغییر دهد.
---
# 118. Suggested URLs — Customer
```text
/login/
/login/otp/
/logout/
/profile/
/tickets/
/tickets/new/
/tickets/<uuid>/
/tickets/<uuid>/reply/
/t/<magic-token>/
```
---
# 119. Suggested URLs — Staff
```text
/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
```text
/messages/<uuid>/edit/
/messages/<uuid>/delete/
/messages/<uuid>/revisions/
```
Permission Server Side اجباری است.
---
# 121. Attachment URL
```text
/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 داشته باشند:
```text
mobile unique
ticket_number unique
public_id unique
one TicketReadState per ticket/user
one active TicketAccessGrant per ticket/user
```
---
# 124. Database Indexes
Index روی:
```text
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 باشند:
```text
Claim Ticket
Transfer
Share/Revoke
Customer Reply + Reopen
Expert Reply + Status Update
Message Edit + Revision
Message Soft Delete
```
---
# 126. Service — create_ticket
ورودی:
```text
customer
category
subject
selected_expert
body
attachments
```
عملیات:
1. Validation
2. Create Ticket
3. Create Initial Message
4. Save Attachments
5. Create TicketEvent
6. Update timestamps
7. Queue Bale Notification
همه عملیات Database باید Transactional باشند.
---
# 127. Service — reply_ticket
بررسی:
```text
can_reply()
```
سپس:
- Create Message
- Attach Files
- Update last activity
- Update waiting status
- Generate Notification Job
- Reopen اگر لازم است
---
# 128. Status بعد از Customer Reply
اگر Ticket:
```text
OPEN
WAITING_CUSTOMER
```
باشد:
```text
WAITING_STAFF
```
شود.
اگر:
```text
RESOLVED
CLOSED
```
باشد:
```text
REOPENED
```
شود.
---
# 129. Status بعد از Staff Reply
بعد از PUBLIC Staff reply:
```text
WAITING_CUSTOMER
```
شود.
اگر اولین Staff reply است:
```text
first_response_at
```
ثبت شود.
---
# 130. Claim Status
هنگام Claim یک Ticket `NEW`:
```text
OPEN
```
شود.
Claim لزوماً Staff Reply محسوب نمی‌شود.
---
# 131. Resolved vs Closed
`RESOLVED`:
کارشناس معتقد است مسئله حل شده ولی Ticket هنوز منطقی قابل ادامه است.
`CLOSED`:
Ticket رسماً بسته شده است.
هر دو با Customer reply می‌توانند Reopen شوند.
---
# 132. Internal Note Status
Internal Note نباید Ticket Status را به:
```text
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:
```text
attempt 1
attempt 2
attempt 3
...
```
با Backoff انجام دهد.
تعداد Max Retry Configurable باشد.
Failure نهایی در Admin قابل مشاهده باشد.
---
# 136. Logging
Application Logging شامل:
```text
request errors
provider errors
notification failures
permission denial
worker errors
file upload failures
```
باشد.
ولی Sensitive Data در Log ماسک شود.
---
# 137. Docker Architecture
Production Compose:
```text
nginx
web
worker
db
```
ساختار:
```text
Internet
↓
Nginx
↓
Gunicorn
↓
Django
↓
PostgreSQL
```
Worker:
```text
PostgreSQL
↑
Django Notification Worker
↓
SMS / Bale APIs
```
---
# 138. Docker Volumes
```text
postgres_data
media_data
static_data
```
Media Volume باید Persistent باشد.
Container Recreation نباید فایل Customer را حذف کند.
---
# 139. Redis
در V1:
```text
Redis = NOT REQUIRED
```
دلایل:
- فقط 20 Expert
- حدود 200 Customer
- WebSocket نداریم
- Cache سنگین نداریم
- Notification Queue می‌تواند DB-backed باشد
---
# 140. زمان اضافه‌شدن Redis
Redis فقط در صورت نیاز واقعی اضافه شود، مثلاً:
```text
WebSocket
High-volume queue
Heavy cache
Distributed rate limiting
Thousands of concurrent sessions
```
---
# 141. Celery
Celery نیز در V1 لازم نیست.
Database Notification Worker کافی است.
اگر Background Tasks بسیار زیاد شدند:
```text
Redis + Celery
```
بدون تغییر Business Logic اصلی قابل اضافه‌شدن باشد.
---
# 142. Backup
چون فایل‌ها Local هستند، Backup بخش حیاتی سیستم است.
Backup باید شامل:
```text
PostgreSQL
Media Volume
Environment configuration backup
```
باشد.
---
# 143. PostgreSQL Backup
حداقل Automated:
```text
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:
```text
Docker Compose
PostgreSQL
Django runserver/Gunicorn optional
Tailwind watcher
```
SQLite فقط برای تست بسیار ساده محلی قابل استفاده است.
Environment اصلی Development و Production بهتر است PostgreSQL باشد تا اختلاف رفتار کاهش یابد.
---
# 147. Database نهایی
انتخاب قطعی:
```text
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
سه سطح اصلی:
```text
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
تست:
```text
valid link
expired link
used link
revoked link
wrong ticket
wrong customer
invalid token
```
---
# 154. OTP Tests
تست:
```text
valid OTP
expired OTP
wrong OTP
maximum attempts
resend cooldown
rate limit
```
---
# 155. Message Tests
تست:
```text
edit
revision creation
soft delete
manager sees deleted content
customer sees tombstone
```
---
# 156. Attachment Tests
تست:
```text
valid type
invalid type
size too large
multiple files
private download
unauthorized download
```
---
# 157. Ticket Status Tests
تمام Transitionها تست شوند.
خصوصاً:
```text
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 شود:
```text
SMS queued
Bale queued
retry
failure
deduplication
```
---
# 159. Coding Standards
پیشنهاد:
```text
Ruff
Black-compatible formatting
Type hints for Services
pytest
pytest-django
```
Business-critical Functions Type Hint داشته باشند.
---
# 160. JavaScript Standards
JavaScript پیچیده Global نوشته نشود.
Componentها جدا:
```text
voice-recorder.js
attachment-uploader.js
ticket-actions.js
mention-autocomplete.js
```
---
# 161. Template Structure
```text
templates/
├── base.html
├── customer/
├── staff/
├── tickets/
│ └── partials/
└── components/
```
HTMX partialها از Page Templateها جدا باشند.
---
# 162. HTMX Conventions
HTMX Endpoint باید در صورت:
```text
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:
```text
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 ایجاد نکنند:
```text
Internal Note
Claim
Transfer
Priority change
Status internal changes
Mention
```
مگر در آینده Policy تغییر کند.
---
# 176. Message Composer Staff
Composer Staff حداقل Mode داشته باشد:
```text
پاسخ به مشتری
یادداشت داخلی
```
رنگ و ظاهر دو 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ها بر اساس:
```text
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 داشته باشد.
استفاده مناسب از:
```python
select_related()
prefetch_related()
```
ضروری است.
---
# 183. Query Count
Ticket List نباید برای هر Ticket Query جدا برای:
- Customer
- Category
- Owner
اجرا کند.
---
# 184. Database JSON
JSONField فقط برای Metadata متغیر مثل:
```text
TicketEvent.metadata
NotificationJob payload
```
استفاده شود.
Core Domain Data داخل JSON ذخیره نشود.
---
# 185. System Configuration
تنظیمات Runtime که مدیر ممکن است تغییر دهد بهتر است DB-driven باشند.
مثلاً:
```text
AttachmentPolicy
SLAConfig
TicketCategory
```
Secrets و Infrastructure settings در Environment بمانند.
---
# 186. Out of Scope V1
موارد زیر در V1 اجرا نشوند مگر Requirement جدید ایجاد شود:
```text
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
- PDF
- 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 محسوب شود که:
1. Business Logic تکمیل باشد.
2. Permission Server-side داشته باشد.
3. Validation داشته باشد.
4. Error UX مناسب داشته باشد.
5. Test داشته باشد.
6. Responsive باشد.
7. Security آن بررسی شده باشد.
8. Logging مناسب داشته باشد.
9. در 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 می‌کند.
سیستم:
```text
CLOSED → REOPENED
```
کند.
Expert مربوطه Notification دریافت کند.
---
# 201. Acceptance Criteria — Internal Note
Expert Note ثبت می‌کند.
- Staff مجاز آن را می‌بینند.
- Customer آن را نمی‌بیند.
- SMS Customer ارسال نمی‌شود.
- Note ظاهری متفاوت دارد.
---
# 202. Acceptance Criteria — Mention
Expert مجاز:
```text
@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 قطعی:
```text
Python
Django
PostgreSQL
Django Templates
HTMX
Alpine.js
Vanilla JavaScript
Tailwind CSS
Gunicorn
Nginx
Docker
Docker Compose
Local Protected Media Storage
```
Notification:
```text
SMS Adapter
Bale Adapter
Database Outbox Worker
```
---
# 205. Technology Not Required
```text
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. معیار معماری
هدف این پروژه ساخت «پیچیده‌ترین معماری ممکن» نیست.
هدف:
```text
Simple
Secure
Maintainable
Testable
Modular
Production-ready
```
است.
هر تکنولوژی جدید باید فقط زمانی اضافه شود که یک مشکل واقعی را حل کند.
برای Scale فعلی، Django + PostgreSQL به تنهایی بیش از اندازه کافی است.
---
# 208. نتیجه نهایی معماری
معماری نهایی:
```text
┌──────────────┐
│ Customer │
└──────┬───────┘
│
OTP / Magic Link
│
▼
┌──────────────┐ ┌───────────────┐
│ Sales Expert │────────▶│ Nginx │
└──────────────┘ └───────┬───────┘
│
▼
┌───────────────┐
│ Django │
│ Modular Mono │
└───────┬───────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌──────────────┐
│ PostgreSQL │ │Local Media │ │Notification │
└────────────┘ └────────────┘ │ Outbox │
└──────┬───────┘
│
▼
┌─────────────────┐
│ Django Worker │
└────────┬────────┘
│
┌─────────────┴────────────┐
▼ ▼
SMS API Bale API
```
این معماری برای نیاز فعلی پروژه ساده، امن، قابل توسعه و کاملاً مناسب است.