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

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. اهداف اصلی

سیستم باید:

  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های منطقی جدا تقسیم می‌شوند.

ساختار پیشنهادی:

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 باید بررسی کند:

  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 باشد.

مثلاً:

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

روش پیشنهادی:

  1. درخواست Download وارد Django می‌شود.
  2. Django Permission را بررسی می‌کند.
  3. در صورت مجاز بودن Header مخصوص Nginx برگردانده می‌شود.
  4. 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

عملیات:

  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

بررسی:

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
  • 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
  • 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 می‌کند.

سیستم:

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

این معماری برای نیاز فعلی پروژه ساده، امن، قابل توسعه و کاملاً مناسب است.