Người vận hành
Quản lý người điều hành trò chuyện trong tổ chức ChatHub của bạn. Người điều hành là những người đại diện xử lý các cuộc trò chuyện của khách hàng.
Tổng quan
Người vận hành trong ChatHub:
- Xử lý các cuộc trò chuyện của khách hàng
- Thuộc các tổ chức cụ thể
- Có trạng thái hoạt động, không hoạt động hoặc đã xóa
- Có thể có mã thông báo xác thực riêng lẻ
- Nhận và trả lời tin nhắn
Danh sách toán tử
Truy xuất tất cả các toán tử cho một tổ chức cụ thể.
Điểm cuối
Yêu cầu
curl -X GET "https://chatapi.smsbat.com/api/operator?organizationId=24" \
-H "Authorization: Bearer {company-token}" \
-H "Accept: text/plain"
Thông số
| Tham số | Loại | Vị trí | Bắt buộc | Mô tả |
|---|---|---|---|---|
Id tổ chức |
số nguyên | Truy vấn | Có | ID tổ chức |
Tiêu đề
| Tiêu đề | Giá trị | Bắt buộc |
|---|---|---|
Ủy quyền |
Người mang | Có |
Chấp nhận |
văn bản/đồng bằng | Có |
Phản hồi
[
{
"id": 101,
"name": "John Smith",
"status": 0,
"organization": {
"id": 24,
"name": "Customer Support"
}
},
{
"id": 102,
"name": "Sarah Johnson",
"status": 0,
"organization": {
"id": 24,
"name": "Customer Support"
}
},
{
"id": 103,
"name": "Mike Wilson",
"status": 1,
"organization": {
"id": 24,
"name": "Customer Support"
}
}
]
Trường phản hồi
| Lĩnh vực | Loại | Mô tả |
|---|---|---|
id |
số nguyên | Mã định danh nhà điều hành duy nhất |
tên |
chuỗi | Tên hiển thị của người vận hành |
trạng thái |
số nguyên | Trạng thái người vận hành (0=Hoạt động, 1=Không hoạt động, 2=Đã xóa) |
tổ chức |
đối tượng | Chi tiết tổ chức phụ huynh |
tổ chức.id |
số nguyên | ID tổ chức |
tổ chức.name |
chuỗi | Tên tổ chức |
Trạng thái nhà điều hành
| Trạng thái | Giá trị | Mô tả |
|---|---|---|
| Đang hoạt động | 0 | Nhà điều hành hiện đang làm việc và có thể nhận các cuộc trò chuyện |
| Không hoạt động | 1 | Nhà điều hành tạm thời bị vô hiệu hóa |
| Đã xóa | 2 | Nhà điều hành đã bị xóa khỏi hệ thống |
Thêm toán tử
Thêm toán tử mới vào tổ chức bằng cách sử dụng điểm cuối đồng bộ hóa.
Điểm cuối
Yêu cầu
curl -X POST https://chatapi.smsbat.com/api/operator/synchronize \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '[
{
"organizationId": 24,
"name": "Alex Brown"
},
{
"organizationId": 24,
"name": "Emma Davis"
}
]'
Nội dung yêu cầu
Mảng đối tượng toán tử:
Trường yêu cầu
| Lĩnh vực | Loại | Bắt buộc | Mô tả |
|---|---|---|---|
Id tổ chức |
số nguyên | Có | ID tổ chức mục tiêu |
tên |
chuỗi | Có | Tên hiển thị của người vận hành |
Phản hồi
[
{
"id": 104,
"name": "Alex Brown",
"status": 0
},
{
"id": 105,
"name": "Emma Davis",
"status": 0
}
]
Thay đổi trạng thái nhà điều hành
Cập nhật trạng thái nhà điều hành (Hoạt động/Không hoạt động/Đã xóa).
Điểm cuối
Yêu cầu
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 1
}'
Nội dung yêu cầu
Trường yêu cầu
| Lĩnh vực | Loại | Bắt buộc | Mô tả |
|---|---|---|---|
id |
số nguyên | Có | ID nhà điều hành |
trạng thái |
số nguyên | Có | Trạng thái mới (0=Hoạt động, 1=Không hoạt động, 2=Đã xóa) |
Phản hồi
Thành công trả về HTTP 200 mà không có nội dung phản hồi.
Giá trị trạng thái
| Trạng thái | Giá trị | Mô tả |
|---|---|---|
| Đang hoạt động | 0 | Nhà điều hành có thể xử lý các cuộc trò chuyện |
| Không hoạt động | 1 | Nhà điều hành tạm thời bị vô hiệu hóa |
| Đã xóa | 2 | Toán tử bị xóa khỏi hệ thống |
Ví dụ: Vô hiệu hóa toán tử
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 1
}'
Ví dụ: Kích hoạt lại toán tử
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 0
}'
Ví dụ: Xóa toán tử
curl -X POST https://chatapi.smsbat.com/api/operator/status \
-H "Authorization: Bearer {company-token}" \
-H "Content-Type: application/json" \
-d '{
"id": 104,
"status": 2
}'
Ví dụ triển khai
Python
import requests
class OperatorManager:
def __init__(self, company_token, base_url='https://chatapi.smsbat.com'):
self.company_token = company_token
self.base_url = base_url
self.headers = {
'Authorization': f'Bearer {company_token}',
'Accept': 'text/plain'
}
def list_operators(self, organization_id):
"""Get all operators for an organization"""
response = requests.get(
f'{self.base_url}/api/operator',
headers=self.headers,
params={'organizationId': organization_id}
)
response.raise_for_status()
return response.json()
def add_operators(self, operators):
"""Add new operators
Args:
operators: List of dicts with organizationId and name
"""
response = requests.post(
f'{self.base_url}/api/operator/synchronize',
headers={**self.headers, 'Content-Type': 'application/json'},
json=operators
)
response.raise_for_status()
return response.json()
def get_active_operators(self, organization_id):
"""Get only active operators"""
operators = self.list_operators(organization_id)
return [op for op in operators if op['status'] == 0]
def find_operator_by_name(self, organization_id, name):
"""Find operator by name"""
operators = self.list_operators(organization_id)
for operator in operators:
if operator['name'].lower() == name.lower():
return operator
return None
def change_operator_status(self, operator_id, status):
"""Change operator status
Args:
operator_id: Operator ID
status: 0 (Active), 1 (Inactive), 2 (Deleted)
"""
response = requests.post(
f'{self.base_url}/api/operator/status',
headers={**self.headers, 'Content-Type': 'application/json'},
json={'id': operator_id, 'status': status}
)
response.raise_for_status()
return response.status_code == 200
# Usage
manager = OperatorManager('your-company-token')
# List operators
operators = manager.list_operators(organization_id=24)
print(f"Found {len(operators)} operators")
# Get only active operators
active = manager.get_active_operators(24)
print(f"{len(active)} active operators")
# Add new operators
new_operators = manager.add_operators([
{'organizationId': 24, 'name': 'John Doe'},
{'organizationId': 24, 'name': 'Jane Smith'}
])
print(f"Added {len(new_operators)} operators")
# Find operator by name
operator = manager.find_operator_by_name(24, 'John Doe')
if operator:
print(f"Found operator: {operator['name']} (ID: {operator['id']})")
# Change operator status
manager.change_operator_status(104, 1) # Deactivate
print("Operator deactivated")
manager.change_operator_status(104, 0) # Reactivate
print("Operator reactivated")
JavaScript (Node.js)
const axios = require('axios');
class OperatorManager {
constructor(companyToken, baseUrl = 'https://chatapi.smsbat.com') {
this.companyToken = companyToken;
this.baseUrl = baseUrl;
this.headers = {
'Authorization': `Bearer ${companyToken}`,
'Accept': 'text/plain'
};
}
async listOperators(organizationId) {
const response = await axios.get(
`${this.baseUrl}/api/operator`,
{
headers: this.headers,
params: { organizationId }
}
);
return response.data;
}
async addOperators(operators) {
const response = await axios.post(
`${this.baseUrl}/api/operator/synchronize`,
operators,
{
headers: {
...this.headers,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
async getActiveOperators(organizationId) {
const operators = await this.listOperators(organizationId);
return operators.filter(op => op.status === 0);
}
async findOperatorByName(organizationId, name) {
const operators = await this.listOperators(organizationId);
return operators.find(op =>
op.name.toLowerCase() === name.toLowerCase()
);
}
async getOperatorById(organizationId, operatorId) {
const operators = await this.listOperators(organizationId);
return operators.find(op => op.id === operatorId);
}
async changeOperatorStatus(operatorId, status) {
const response = await axios.post(
`${this.baseUrl}/api/operator/status`,
{ id: operatorId, status },
{
headers: {
...this.headers,
'Content-Type': 'application/json'
}
}
);
return response.status === 200;
}
}
// Usage
const manager = new OperatorManager('your-company-token');
async function manageOperators() {
// List operators
const operators = await manager.listOperators(24);
console.log(`Found ${operators.length} operators`);
// Get active operators
const active = await manager.getActiveOperators(24);
console.log(`${active.length} active operators`);
// Add new operators
const newOperators = await manager.addOperators([
{ organizationId: 24, name: 'John Doe' },
{ organizationId: 24, name: 'Jane Smith' }
]);
console.log(`Added ${newOperators.length} operators`);
// Find operator by name
const operator = await manager.findOperatorByName(24, 'John Doe');
if (operator) {
console.log(`Found: ${operator.name} (ID: ${operator.id})`);
}
// Change operator status
await manager.changeOperatorStatus(104, 1); // Deactivate
console.log('Operator deactivated');
await manager.changeOperatorStatus(104, 0); // Reactivate
console.log('Operator reactivated');
}
manageOperators();
PHP
<?php
class OperatorManager {
private $companyToken;
private $baseUrl;
public function __construct($companyToken, $baseUrl = 'https://chatapi.smsbat.com') {
$this->companyToken = $companyToken;
$this->baseUrl = $baseUrl;
}
public function listOperators($organizationId) {
$url = $this->baseUrl . '/api/operator?organizationId=' . $organizationId;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->companyToken,
'Accept: text/plain'
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function addOperators($operators) {
$ch = curl_init($this->baseUrl . '/api/operator/synchronize');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->companyToken,
'Content-Type: application/json',
'Accept: text/plain'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($operators));
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function getActiveOperators($organizationId) {
$operators = $this->listOperators($organizationId);
return array_filter($operators, function($op) {
return $op['status'] === 0;
});
}
public function findOperatorByName($organizationId, $name) {
$operators = $this->listOperators($organizationId);
foreach ($operators as $operator) {
if (strcasecmp($operator['name'], $name) === 0) {
return $operator;
}
}
return null;
}
public function changeOperatorStatus($operatorId, $status) {
$ch = curl_init($this->baseUrl . '/api/operator/status');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->companyToken,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'id' => $operatorId,
'status' => $status
]));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $httpCode === 200;
}
}
// Usage
$manager = new OperatorManager('your-company-token');
// List operators
$operators = $manager->listOperators(24);
echo "Found " . count($operators) . " operators\n";
// Get active operators
$active = $manager->getActiveOperators(24);
echo count($active) . " active operators\n";
// Add new operators
$newOperators = $manager->addOperators([
['organizationId' => 24, 'name' => 'John Doe'],
['organizationId' => 24, 'name' => 'Jane Smith']
]);
echo "Added " . count($newOperators) . " operators\n";
// Find operator
$operator = $manager->findOperatorByName(24, 'John Doe');
if ($operator) {
echo "Found: " . $operator['name'] . " (ID: " . $operator['id'] . ")\n";
}
// Change operator status
$manager->changeOperatorStatus(104, 1); // Deactivate
echo "Operator deactivated\n";
$manager->changeOperatorStatus(104, 0); // Reactivate
echo "Operator reactivated\n";
Các trường hợp sử dụng phổ biến
Tham gia thành viên nhóm mới
async function onboardOperators(teamMembers, organizationId) {
const operators = teamMembers.map(member => ({
organizationId,
name: member.fullName
}));
const created = await addOperators(operators);
// Generate tokens for each operator
for (const operator of created) {
const token = await getOperatorToken(operator.id);
await sendWelcomeEmail(operator, token);
}
return created;
}
Giám sát trạng thái người vận hành
async function getOperatorStatistics(organizationId) {
const operators = await listOperators(organizationId);
return {
total: operators.length,
active: operators.filter(op => op.status === 0).length,
inactive: operators.filter(op => op.status === 1).length,
deleted: operators.filter(op => op.status === 2).length
};
}
Cân bằng tải
async function assignChatToOperator(organizationId, chatId) {
const activeOperators = await getActiveOperators(organizationId);
if (activeOperators.length === 0) {
throw new Error('No active operators available');
}
// Simple round-robin assignment
const operatorIndex = chatId % activeOperators.length;
return activeOperators[operatorIndex];
}
Nhập số lượng lớn
async function importOperatorsFromCSV(csvData, organizationId) {
const lines = csvData.split('\n').slice(1); // Skip header
const operators = lines
.filter(line => line.trim())
.map(line => {
const [name] = line.split(',');
return { organizationId, name: name.trim() };
});
// Batch import in chunks of 100
const chunkSize = 100;
const results = [];
for (let i = 0; i < operators.length; i += chunkSize) {
const chunk = operators.slice(i, i + chunkSize);
const imported = await addOperators(chunk);
results.push(...imported);
console.log(`Imported ${results.length}/${operators.length}`);
}
return results;
}
Các phương pháp hay nhất
Xử lý lỗi
async function addOperatorsSafely(operators) {
try {
return await addOperators(operators);
} catch (error) {
if (error.response?.status === 400) {
console.error('Invalid operator data:', error.response.data);
// Handle validation errors
} else if (error.response?.status === 401) {
console.error('Authentication failed');
// Refresh token
} else {
console.error('Unexpected error:', error);
}
throw error;
}
}
Bộ nhớ đệm
class CachedOperatorManager extends OperatorManager {
constructor(companyToken) {
super(companyToken);
this.cache = new Map();
this.cacheTTL = 60000; // 1 minute
}
async listOperators(organizationId, useCache = true) {
const cacheKey = `org_${organizationId}`;
const cached = this.cache.get(cacheKey);
if (useCache && cached && Date.now() - cached.time < this.cacheTTL) {
return cached.data;
}
const data = await super.listOperators(organizationId);
this.cache.set(cacheKey, {
data,
time: Date.now()
});
return data;
}
clearCache(organizationId = null) {
if (organizationId) {
this.cache.delete(`org_${organizationId}`);
} else {
this.cache.clear();
}
}
}
Xác thực
function validateOperatorData(operators) {
const errors = [];
operators.forEach((op, index) => {
if (!op.organizationId) {
errors.push(`Operator ${index}: Missing organizationId`);
}
if (!op.name || op.name.trim().length === 0) {
errors.push(`Operator ${index}: Name is required`);
}
if (op.name && op.name.length > 100) {
errors.push(`Operator ${index}: Name too long (max 100 chars)`);
}
});
if (errors.length > 0) {
throw new Error('Validation failed:\n' + errors.join('\n'));
}
}
// Usage
try {
validateOperatorData(operatorData);
await addOperators(operatorData);
} catch (error) {
console.error(error.message);
}
Giới hạn tỷ lệ
class RateLimitedOperatorManager extends OperatorManager {
constructor(companyToken, requestsPerSecond = 5) {
super(companyToken);
this.minInterval = 1000 / requestsPerSecond;
this.lastRequest = 0;
}
async throttle() {
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequest;
if (timeSinceLastRequest < this.minInterval) {
await new Promise(resolve =>
setTimeout(resolve, this.minInterval - timeSinceLastRequest)
);
}
this.lastRequest = Date.now();
}
async listOperators(organizationId) {
await this.throttle();
return super.listOperators(organizationId);
}
async addOperators(operators) {
await this.throttle();
return super.addOperators(operators);
}
}
Khắc phục sự cố
Không có toán tử nào được trả lại
- Xác minh ID tổ chức là chính xác
- Kiểm tra tổ chức tồn tại và có người vận hành
- Đảm bảo mã thông báo của công ty có quyền truy cập vào tổ chức
Không thêm được toán tử
- Xác minh ID tổ chức tồn tại
- Kiểm tra định dạng tên nhà điều hành
- Đảm bảo mã thông báo của công ty hợp lệ
- Xác minh định dạng JSON là chính xác
401 trái phép
- Xác minh mã thông báo của công ty là hợp lệ
- Mã kiểm tra chưa hết hạn
- Yêu cầu mã thông báo mới nếu cần
Toán tử trùng lặp
Điểm cuối đồng bộ hóa có thể cho phép trùng lặp tên. Thực hiện chống trùng lặp:
async function addUniqueOperators(newOperators, organizationId) {
const existing = await listOperators(organizationId);
const existingNames = new Set(
existing.map(op => op.name.toLowerCase())
);
const unique = newOperators.filter(op =>
!existingNames.has(op.name.toLowerCase())
);
if (unique.length === 0) {
return [];
}
return await addOperators(unique);
}
Các bước tiếp theo
- Xác thực - Nhận mã thông báo của nhà điều hành
- Tổ chức - Quản lý tổ chức
- Tích hợp tiện ích - Tích hợp tiện ích trò chuyện