Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/docs/SUPPORT_OPERATORS_MANAGEMENT_SCENARIO.md
2026-04-14 19:34:55 +03:30

918 lines
31 KiB
Markdown
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# سناریوی پیاده‌سازی: مدیریت اپراتورهای پشتیبانی
## 📋 خلاصه اجرایی
این سند سناریوی کامل پیاده‌سازی بخش مدیریت App-Level Permissions (با تمرکز روی اپراتورهای پشتیبانی) را شامل می‌شود.
---
## 🎯 اهداف
1. **مدیریت مرکزی:** ایجاد یک پنل مدیریتی برای تخصیص و لغو permissions در سطح اپلیکیشن
2. **امنیت:** فقط SuperAdmin بتواند App Permissions را مدیریت کند
3. **ساده و کاربرپسند:** UI ساده و واضح برای مدیریت نقش‌ها
4. **قابل توسعه:** طراحی به گونه‌ای که بتوان permissions جدید اضافه کرد
---
## 🏗️ معماری کلی
```
┌─────────────────────────────────────────────────────────────┐
│ SuperAdmin Panel (UI) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ User Management │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ • List Users │ │ │
│ │ │ • View User Details │ │ │
│ │ │ • Manage App Permissions ← NEW! │ │ │
│ │ │ - Support Operator │ │ │
│ │ │ - System Settings │ │ │
│ │ │ - User Management │ │ │
│ │ │ - Business Management │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕ API Calls
┌─────────────────────────────────────────────────────────────┐
│ Backend API │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ /api/v1/admin/users/{user_id}/app-permissions │ │
│ │ • GET: دریافت permissions کاربر │ │
│ │ • PUT: به‌روزرسانی permissions کاربر ← NEW! │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ /api/v1/admin/operators ← NEW! │ │
│ │ • GET: لیست اپراتورهای پشتیبانی │ │
│ │ • POST: اضافه کردن اپراتور │ │
│ │ • DELETE: حذف اپراتور │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 📍 فاز 1: Backend API (اولویت بالا)
### 1.1 ایجاد Endpoint های جدید
**فایل:** `hesabixAPI/adapters/api/v1/admin/users_permissions.py` (جدید)
```python
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from typing import Dict, Any, List
from pydantic import BaseModel, Field
from adapters.db.session import get_db
from adapters.db.models.user import User
from adapters.db.repositories.user_repo import UserRepository
from app.core.auth_dependency import get_current_user, AuthContext
from app.core.permissions import require_app_permission
from app.core.responses import success_response
router = APIRouter(
prefix="/admin/users",
tags=["مدیریت کاربران", "مدیریت سیستم"]
)
# ========== Schemas ==========
class AppPermissionsResponse(BaseModel):
"""پاسخ دریافت App Permissions"""
user_id: int
email: str
app_permissions: Dict[str, bool] = Field(default_factory=dict)
class Config:
from_attributes = True
class UpdateAppPermissionsRequest(BaseModel):
"""درخواست به‌روزرسانی App Permissions"""
permissions: Dict[str, bool] = Field(
...,
description="دسترسی‌های سطح اپلیکیشن (مثال: {'support_operator': true, 'system_settings': false})"
)
class Config:
json_schema_extra = {
"example": {
"permissions": {
"support_operator": True,
"system_settings": False,
"user_management": False,
"business_management": False
}
}
}
class OperatorSummary(BaseModel):
"""خلاصه اطلاعات اپراتور"""
id: int
email: str
first_name: str | None
last_name: str | None
full_name: str | None
telegram_chat_id: str | None
is_active: bool
created_at: str
class Config:
from_attributes = True
# ========== Endpoints ==========
@router.get(
"/{user_id}/app-permissions",
summary="دریافت App Permissions کاربر",
description="دریافت دسترسی‌های سطح اپلیکیشن یک کاربر. نیاز به مجوز SuperAdmin دارد.",
response_model=AppPermissionsResponse
)
@require_app_permission("superadmin")
async def get_user_app_permissions(
user_id: int,
request: Request,
ctx: AuthContext = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""دریافت App Permissions کاربر"""
user = db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="کاربر یافت نشد")
return success_response(
data={
"user_id": user.id,
"email": user.email,
"app_permissions": user.app_permissions or {}
},
request=request
)
@router.put(
"/{user_id}/app-permissions",
summary="به‌روزرسانی App Permissions کاربر",
description="به‌روزرسانی دسترسی‌های سطح اپلیکیشن یک کاربر. فقط SuperAdmin.",
)
@require_app_permission("superadmin")
async def update_user_app_permissions(
user_id: int,
permissions_request: UpdateAppPermissionsRequest,
request: Request,
ctx: AuthContext = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""به‌روزرسانی App Permissions کاربر"""
# بررسی وجود کاربر
user = db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="کاربر یافت نشد")
# جلوگیری از تغییر permissions خودش
if user.id == ctx.get_user_id():
raise HTTPException(
status_code=403,
detail="شما نمی‌توانید دسترسی‌های خود را تغییر دهید"
)
# فیلتر کردن فقط permissions معتبر
valid_permissions = {
"superadmin",
"support_operator",
"system_settings",
"user_management",
"business_management"
}
# فقط permissions که true هستند را نگه می‌داریم
new_permissions = {
k: v for k, v in permissions_request.permissions.items()
if k in valid_permissions and v is True
}
# به‌روزرسانی
user.app_permissions = new_permissions
db.commit()
db.refresh(user)
return success_response(
data={
"user_id": user.id,
"email": user.email,
"app_permissions": user.app_permissions or {}
},
request=request,
message="دسترسی‌ها با موفقیت به‌روزرسانی شد"
)
# ========== Operator Management Endpoints ==========
@router.get(
"/operators",
summary="لیست اپراتورهای پشتیبانی",
description="دریافت لیست تمام اپراتورهای پشتیبانی فعال",
response_model=List[OperatorSummary]
)
@require_app_permission("superadmin")
async def list_support_operators(
request: Request,
ctx: AuthContext = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""لیست اپراتورهای پشتیبانی"""
user_repo = UserRepository(db)
operators = user_repo.get_support_operators()
operators_data = []
for op in operators:
full_name = None
if op.first_name or op.last_name:
parts = [p for p in [op.first_name, op.last_name] if p]
full_name = " ".join(parts) if parts else None
operators_data.append({
"id": op.id,
"email": op.email,
"first_name": op.first_name,
"last_name": op.last_name,
"full_name": full_name,
"telegram_chat_id": op.telegram_chat_id,
"is_active": op.is_active,
"created_at": op.created_at.isoformat()
})
return success_response(
data={"items": operators_data, "total": len(operators_data)},
request=request
)
@router.post(
"/operators/{user_id}",
summary="اضافه کردن اپراتور پشتیبانی",
description="افزودن مجوز اپراتور پشتیبانی به یک کاربر"
)
@require_app_permission("superadmin")
async def add_support_operator(
user_id: int,
request: Request,
ctx: AuthContext = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""اضافه کردن اپراتور پشتیبانی"""
user = db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="کاربر یافت نشد")
# اضافه کردن permission
permissions = user.app_permissions or {}
permissions['support_operator'] = True
user.app_permissions = permissions
db.commit()
db.refresh(user)
return success_response(
data={"user_id": user.id, "email": user.email},
request=request,
message="کاربر به عنوان اپراتور پشتیبانی اضافه شد"
)
@router.delete(
"/operators/{user_id}",
summary="حذف اپراتور پشتیبانی",
description="لغو مجوز اپراتور پشتیبانی از یک کاربر"
)
@require_app_permission("superadmin")
async def remove_support_operator(
user_id: int,
request: Request,
ctx: AuthContext = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""حذف اپراتور پشتیبانی"""
user = db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="کاربر یافت نشد")
# حذف permission
if user.app_permissions and 'support_operator' in user.app_permissions:
permissions = user.app_permissions.copy()
del permissions['support_operator']
user.app_permissions = permissions if permissions else {}
db.commit()
db.refresh(user)
return success_response(
data={"user_id": user.id, "email": user.email},
request=request,
message="مجوز اپراتور پشتیبانی لغو شد"
)
```
### 1.2 ثبت Router جدید
**فایل:** `hesabixAPI/app/main.py`
```python
# اضافه کردن import
from adapters.api.v1.admin import users_permissions
# در تابع create_app():
app.include_router(
users_permissions.router,
prefix="/api/v1",
)
```
### 1.3 تست API
```bash
# دریافت App Permissions کاربر
curl -X GET "http://localhost:8000/api/v1/admin/users/1/app-permissions" \
-H "Authorization: Bearer YOUR_TOKEN"
# به‌روزرسانی App Permissions
curl -X PUT "http://localhost:8000/api/v1/admin/users/1/app-permissions" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permissions": {
"support_operator": true,
"system_settings": false
}
}'
# لیست اپراتورها
curl -X GET "http://localhost:8000/api/v1/admin/users/operators" \
-H "Authorization: Bearer YOUR_TOKEN"
# اضافه کردن اپراتور
curl -X POST "http://localhost:8000/api/v1/admin/users/operators/1" \
-H "Authorization: Bearer YOUR_TOKEN"
# حذف اپراتور
curl -X DELETE "http://localhost:8000/api/v1/admin/users/operators/1" \
-H "Authorization: Bearer YOUR_TOKEN"
```
---
## 📍 فاز 2: Frontend UI (اولویت متوسط)
### 2.1 سرویس API در Flutter
**فایل:** `hesabixUI/hesabix_ui/lib/services/admin_users_service.dart` (جدید)
```dart
import '../core/api_client.dart';
class AdminUsersService {
final ApiClient _api;
AdminUsersService(this._api);
/// دریافت App Permissions کاربر
Future<Map<String, dynamic>> getUserAppPermissions(int userId) async {
final res = await _api.get<Map<String, dynamic>>(
'/api/v1/admin/users/$userId/app-permissions'
);
return Map<String, dynamic>.from(res.data?['data'] as Map? ?? const {});
}
/// به‌روزرسانی App Permissions کاربر
Future<Map<String, dynamic>> updateUserAppPermissions(
int userId,
Map<String, bool> permissions
) async {
final res = await _api.put<Map<String, dynamic>>(
'/api/v1/admin/users/$userId/app-permissions',
data: {'permissions': permissions}
);
return Map<String, dynamic>.from(res.data?['data'] as Map? ?? const {});
}
/// لیست اپراتورهای پشتیبانی
Future<List<Map<String, dynamic>>> listSupportOperators() async {
final res = await _api.get<Map<String, dynamic>>(
'/api/v1/admin/users/operators'
);
final data = res.data?['data'] as Map? ?? {};
final items = data['items'] as List? ?? [];
return items.map((item) => Map<String, dynamic>.from(item as Map)).toList();
}
/// اضافه کردن اپراتور
Future<void> addSupportOperator(int userId) async {
await _api.post(
'/api/v1/admin/users/operators/$userId',
);
}
/// حذف اپراتور
Future<void> removeSupportOperator(int userId) async {
await _api.delete(
'/api/v1/admin/users/operators/$userId',
);
}
}
```
### 2.2 صفحه مدیریت دسترسی‌ها
**فایل:** `hesabixUI/hesabix_ui/lib/pages/admin/user_app_permissions_page.dart` (جدید)
```dart
import 'package:flutter/material.dart';
import 'package:hesabix_ui/core/api_client.dart';
import 'package:hesabix_ui/services/admin_users_service.dart';
class UserAppPermissionsPage extends StatefulWidget {
final int userId;
final String userEmail;
const UserAppPermissionsPage({
super.key,
required this.userId,
required this.userEmail,
});
@override
State<UserAppPermissionsPage> createState() => _UserAppPermissionsPageState();
}
class _UserAppPermissionsPageState extends State<UserAppPermissionsPage> {
final _service = AdminUsersService(ApiClient());
bool _isLoading = true;
Map<String, bool> _permissions = {};
final Map<String, String> _permissionLabels = {
'support_operator': 'اپراتور پشتیبانی',
'system_settings': 'تنظیمات سیستم',
'user_management': 'مدیریت کاربران',
'business_management': 'مدیریت کسب‌وکارها',
};
final Map<String, String> _permissionDescriptions = {
'support_operator': 'دسترسی به پنل پشتیبانی و مدیریت تیکت‌ها',
'system_settings': 'دسترسی به تنظیمات سیستم',
'user_management': 'مدیریت کاربران در سطح اپلیکیشن',
'business_management': 'مدیریت کسب‌وکارها',
};
@override
void initState() {
super.initState();
_loadPermissions();
}
Future<void> _loadPermissions() async {
setState(() => _isLoading = true);
try {
final data = await _service.getUserAppPermissions(widget.userId);
final appPerms = data['app_permissions'] as Map? ?? {};
// Initialize all permissions
final permissions = <String, bool>{};
for (var key in _permissionLabels.keys) {
permissions[key] = appPerms[key] == true;
}
setState(() {
_permissions = permissions;
_isLoading = false;
});
} catch (e) {
setState(() => _isLoading = false);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('خطا در بارگذاری دسترسی‌ها: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
Future<void> _savePermissions() async {
try {
await _service.updateUserAppPermissions(widget.userId, _permissions);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('دسترسی‌ها با موفقیت ذخیره شد'),
backgroundColor: Colors.green,
),
);
Navigator.of(context).pop(true); // Return true to indicate success
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('خطا در ذخیره دسترسی‌ها: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('مدیریت دسترسی‌ها'),
actions: [
TextButton.icon(
onPressed: _isLoading ? null : _savePermissions,
icon: const Icon(Icons.save, color: Colors.white),
label: const Text('ذخیره', style: TextStyle(color: Colors.white)),
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// User Info Card
Card(
child: ListTile(
leading: CircleAvatar(
child: Text(widget.userEmail[0].toUpperCase()),
),
title: Text(widget.userEmail),
subtitle: const Text('مدیریت دسترسی‌های سطح اپلیکیشن'),
),
),
const SizedBox(height: 24),
// Permissions List
const Text(
'دسترسی‌های سطح اپلیکیشن',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
..._permissionLabels.entries.map((entry) {
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: SwitchListTile(
title: Text(entry.value),
subtitle: Text(_permissionDescriptions[entry.key] ?? ''),
value: _permissions[entry.key] ?? false,
onChanged: (value) {
setState(() {
_permissions[entry.key] = value;
});
},
secondary: Icon(
_getIconForPermission(entry.key),
color: Theme.of(context).primaryColor,
),
),
);
}),
],
),
),
);
}
IconData _getIconForPermission(String permission) {
switch (permission) {
case 'support_operator':
return Icons.support_agent;
case 'system_settings':
return Icons.settings;
case 'user_management':
return Icons.people;
case 'business_management':
return Icons.business;
default:
return Icons.check_circle;
}
}
}
```
### 2.3 صفحه لیست اپراتورها
**فایل:** `hesabixUI/hesabix_ui/lib/pages/admin/support_operators_page.dart` (جدید)
```dart
import 'package:flutter/material.dart';
import 'package:hesabix_ui/core/api_client.dart';
import 'package:hesabix_ui/services/admin_users_service.dart';
class SupportOperatorsPage extends StatefulWidget {
const SupportOperatorsPage({super.key});
@override
State<SupportOperatorsPage> createState() => _SupportOperatorsPageState();
}
class _SupportOperatorsPageState extends State<SupportOperatorsPage> {
final _service = AdminUsersService(ApiClient());
bool _isLoading = true;
List<Map<String, dynamic>> _operators = [];
@override
void initState() {
super.initState();
_loadOperators();
}
Future<void> _loadOperators() async {
setState(() => _isLoading = true);
try {
final operators = await _service.listSupportOperators();
setState(() {
_operators = operators;
_isLoading = false;
});
} catch (e) {
setState(() => _isLoading = false);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('خطا در بارگذاری لیست اپراتورها: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
Future<void> _removeOperator(int userId, String email) async {
final confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('حذف اپراتور'),
content: Text('آیا مطمئن هستید که می‌خواهید دسترسی اپراتور را از $email لغو کنید؟'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('انصراف'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text('حذف'),
),
],
),
);
if (confirm == true) {
try {
await _service.removeSupportOperator(userId);
await _loadOperators();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('دسترسی اپراتور با موفقیت لغو شد'),
backgroundColor: Colors.green,
),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('خطا در حذف اپراتور: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('اپراتورهای پشتیبانی'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _loadOperators,
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _operators.isEmpty
? const Center(
child: Text('هیچ اپراتور پشتیبانی یافت نشد'),
)
: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _operators.length,
itemBuilder: (context, index) {
final operator = _operators[index];
final fullName = operator['full_name'] as String?;
final email = operator['email'] as String;
final telegramId = operator['telegram_chat_id'] as String?;
final isActive = operator['is_active'] as bool? ?? false;
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
leading: CircleAvatar(
backgroundColor: isActive
? Colors.green
: Colors.grey,
child: Icon(
Icons.support_agent,
color: Colors.white,
),
),
title: Text(fullName ?? email),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (fullName != null) Text(email),
if (telegramId != null)
Row(
children: [
const Icon(Icons.telegram, size: 16, color: Colors.blue),
const SizedBox(width: 4),
Text('متصل به تلگرام'),
],
),
if (!isActive)
const Text(
'غیرفعال',
style: TextStyle(color: Colors.red),
),
],
),
trailing: IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => _removeOperator(
operator['id'] as int,
email,
),
),
),
);
},
),
);
}
}
```
### 2.4 افزودن به منوی تنظیمات
**فایل:** `hesabixUI/hesabix_ui/lib/pages/system_settings/services/settings_categorization_service.dart`
```dart
// در بخش items دسته 'users_businesses' (خط 154)، اضافه کنید:
SettingsItem(
id: 'support_operators',
title: 'settingsSupportOperators',
description: 'settingsSupportOperatorsDescription',
icon: Icons.support_agent_outlined,
color: const Color(0xFFE91E63),
route: '/user/profile/system-settings/support-operators',
categoryId: 'users_businesses',
order: 3,
),
```
### 2.5 افزودن Route ها
**فایل:** `hesabixUI/hesabix_ui/lib/main.dart`
```dart
// در بخش system-settings routes
GoRoute(
path: 'support-operators',
name: 'system_settings_support_operators',
builder: (context, state) {
return const SupportOperatorsPage();
},
),
```
---
## 📍 فاز 3: اضافه کردن به صفحه لیست کاربران (اولویت پایین)
در صفحه لیست کاربران (`/api/v1/users`) می‌توانید یک دکمه "مدیریت دسترسی‌ها" اضافه کنید که به صفحه `UserAppPermissionsPage` هدایت کند.
---
## 🔒 نکات امنیتی
1. **فقط SuperAdmin:** تمام endpoint ها باید با `@require_app_permission("superadmin")` محافظت شوند
2. **جلوگیری از خودکاری:** کاربر نباید بتواند permissions خودش را تغییر دهد
3. **Audit Log:** تغییرات permissions باید در activity_logs ثبت شوند
4. **Validation:** فقط permissions معتبر باید پذیرفته شوند
---
## 📊 اولویت‌بندی پیاده‌سازی
### اولویت 1 (ضروری - همین حالا):
1. ✅ Backend API endpoints برای مدیریت App Permissions
2. ✅ تست کامل API ها
### اولویت 2 (مهم - هفته آینده):
3. ✅ UI صفحه مدیریت دسترسی‌ها
4. ✅ UI صفحه لیست اپراتورها
5. ✅ اتصال به منوی تنظیمات
### اولویت 3 (خوب است داشته باشیم - ماه آینده):
6. ⬜ Audit logging برای تغییرات
7. ⬜ یکپارچه‌سازی با صفحه لیست کاربران
8. ⬜ فیلتر و جستجو در لیست اپراتورها
---
## ✅ Checklist پیاده‌سازی
### Backend:
- [ ] ایجاد فایل `users_permissions.py`
- [ ] اضافه کردن Schemas (Pydantic models)
- [ ] پیاده‌سازی endpoint های CRUD
- [ ] ثبت router در `main.py`
- [ ] تست با Postman/curl
- [ ] نوشتن Unit Tests
### Frontend:
- [ ] ایجاد `admin_users_service.dart`
- [ ] پیاده‌سازی `UserAppPermissionsPage`
- [ ] پیاده‌سازی `SupportOperatorsPage`
- [ ] افزودن به `settings_categorization_service.dart`
- [ ] افزودن routes
- [ ] افزودن translations
- [ ] تست در محیط توسعه
### Documentation:
- [ ] به‌روزرسانی `PERMISSIONS_SYSTEM.md`
- [ ] نوشتن مستندات API
- [ ] نوشتن راهنمای کاربر
---
## 🚀 نحوه استفاده بعد از پیاده‌سازی
### برای SuperAdmin:
1. **افزودن اپراتور:**
- وارد System Settings > Support Operators شوید
- روی "افزودن اپراتور" کلیک کنید
- کاربر مورد نظر را انتخاب کنید
2. **مدیریت دسترسی‌ها:**
- وارد System Settings > User Management شوید
- روی کاربر مورد نظر کلیک کنید
- "مدیریت دسترسی‌ها" را انتخاب کنید
- دسترسی‌های مورد نظر را فعال/غیرفعال کنید
- ذخیره کنید
3. **حذف اپراتور:**
- وارد System Settings > Support Operators شوید
- روی دکمه حذف کنار نام اپراتور کلیک کنید
- تأیید کنید
---
## 📞 پشتیبانی و سوالات
در صورت بروز مشکل یا سوال:
- مستندات API: `/docs` (Swagger UI)
- لاگ‌های سیستم: `journalctl -u hesabix-api -f`
- لاگ‌های اپلیکیشن: فایل‌های log در `hesabixAPI/logs/`
---
**نکته مهم:** این سناریو قابل توسعه است و می‌توان permissions دیگری نیز به همین روش اضافه کرد.