完整版V1 加入爬虫功能
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
-- 工单云控同步:Node 服务地址与各类型同步周期(分钟,0=不自动同步)
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_scrm_node_host', 'split', '云控 Node 服务地址', '所有云控蜘蛛共用的 Headless 服务根地址', 'string', '', 'http://127.0.0.1:3001', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_scrm_node_host' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_fail_pause_threshold', 'split', '连续同步失败暂停阈值', '连续同步失败达到该次数后自动关闭工单并暂停定时同步;同步成功后将清零重新计数;0 表示不因失败自动暂停', 'number', '', '5', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_fail_pause_threshold' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_a2c', 'split', 'A2C云控同步周期(分钟)', '0 表示不自动同步', 'number', '', '5', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_a2c' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_haiwang', 'split', '海王同步周期(分钟)', '0 表示不自动同步', 'number', '', '5', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_haiwang' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_huojian', 'split', '火箭云控同步周期(分钟)', '0 表示不自动同步', 'number', '', '5', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_huojian' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_xinghe', 'split', '星河云控同步周期(分钟)', '0 表示不自动同步', 'number', '', '5', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_xinghe' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_ss_customer', 'split', 'SS云控(Customer)同步周期(分钟)', '0 表示不自动同步', 'number', '', '5', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_ss_customer' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_ceo_scrm', 'split', 'CEO SCRM同步周期(分钟)', '0 表示不自动同步(蜘蛛未实现)', 'number', '', '0', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_ceo_scrm' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_taiji', 'split', '太极云控同步周期(分钟)', '0 表示不自动同步', 'number', '', '0', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_taiji' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_ss_channel', 'split', 'SS云控(Channel)同步周期(分钟)', '0 表示不自动同步', 'number', '', '0', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_ss_channel' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_yifafa', 'split', '译发发云控同步周期(分钟)', '0 表示不自动同步', 'number', '', '0', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_yifafa' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_whatshub', 'split', 'Whatshub云控同步周期(分钟)', '0 表示不自动同步', 'number', '', '0', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_whatshub' LIMIT 1);
|
||||
|
||||
INSERT INTO `fa_config` (`name`, `group`, `title`, `tip`, `type`, `visible`, `value`, `content`, `rule`, `extend`, `setting`)
|
||||
SELECT 'split_sync_interval_sihai', 'split', '四海云控同步周期(分钟)', '0 表示不自动同步', 'number', '', '0', '', '', '', NULL
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fa_config` WHERE `name` = 'split_sync_interval_sihai' LIMIT 1);
|
||||
@@ -15,6 +15,7 @@ CREATE TABLE `fa_split_number` (
|
||||
`inbound_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '进线人数',
|
||||
`manual_manage` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '手动管理:0=否,1=是',
|
||||
`status` enum('normal','hidden') NOT NULL DEFAULT 'normal' COMMENT '状态:normal=正常,hidden=停用',
|
||||
`weigh` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序权重(越大越靠前)',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
@@ -22,6 +23,7 @@ CREATE TABLE `fa_split_number` (
|
||||
KEY `admin_id` (`admin_id`),
|
||||
KEY `split_link_id` (`split_link_id`),
|
||||
KEY `status` (`status`),
|
||||
KEY `weigh` (`weigh`),
|
||||
KEY `number` (`number`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='分流号码表';
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
-- 号码表:排序权重(可选,跳转轮转按 id 顺序;随机打乱通过新号码随机插入顺序实现)
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
ALTER TABLE `fa_split_number`
|
||||
ADD COLUMN `weigh` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序权重(越大越靠前)' AFTER `status`;
|
||||
|
||||
UPDATE `fa_split_number` SET `weigh` = `id` WHERE `weigh` = 0;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- 工单云控定时同步:扩展字段 + 手动同步权限节点
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
ALTER TABLE `fa_split_ticket`
|
||||
ADD COLUMN `sync_fail_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '连续同步失败次数' AFTER `sync_message`,
|
||||
ADD COLUMN `speed_snapshot_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '速度计算基准完成数' AFTER `sync_fail_count`,
|
||||
ADD COLUMN `speed_snapshot_time` bigint(16) DEFAULT NULL COMMENT '速度计算基准时间' AFTER `speed_snapshot_count`;
|
||||
|
||||
ALTER TABLE `fa_split_number`
|
||||
ADD COLUMN `platform_status` enum('online','offline','unknown') NOT NULL DEFAULT 'unknown' COMMENT '云控平台在线状态' AFTER `manual_manage`,
|
||||
ADD COLUMN `last_sync_visit_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上次同步时访问次数' AFTER `platform_status`,
|
||||
ADD COLUMN `last_sync_inbound_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上次同步时进线人数' AFTER `last_sync_visit_count`,
|
||||
ADD COLUMN `no_inbound_click_streak` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '进线无增长期间连续点击增量' AFTER `last_sync_inbound_count`;
|
||||
|
||||
INSERT INTO `fa_auth_rule` (`type`, `pid`, `name`, `title`, `icon`, `condition`, `remark`, `ismenu`, `createtime`, `updatetime`, `weigh`, `status`)
|
||||
SELECT 'file', m.id, 'split.ticket/sync', '同步状态', 'fa fa-circle-o', '', '', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 'normal'
|
||||
FROM `fa_auth_rule` m
|
||||
WHERE m.name = 'split.ticket' AND m.ismenu = 1
|
||||
AND NOT EXISTS (SELECT 1 FROM `fa_auth_rule` WHERE `name` = 'split.ticket/sync' LIMIT 1)
|
||||
LIMIT 1;
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
use app\common\service\SplitTicketSyncLogger;
|
||||
use app\common\service\SplitTicketSyncService;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
|
||||
/**
|
||||
* 工单云控定时同步 CLI
|
||||
*
|
||||
* 用法:
|
||||
* php think split:sync-tickets
|
||||
* php think split:sync-tickets --ticket=12
|
||||
*/
|
||||
class SplitSyncTickets extends Command
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName('split:sync-tickets')
|
||||
->addOption('ticket', 't', Option::VALUE_OPTIONAL, '指定工单 ID(强制同步,忽略周期)', '')
|
||||
->setDescription('同步分流工单云控数据');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output): void
|
||||
{
|
||||
set_time_limit(0);
|
||||
SplitTicketSyncLogger::log('cli', 'command start', [
|
||||
'ticketOption' => trim((string) $input->getOption('ticket')),
|
||||
'appDebug' => SplitTicketSyncLogger::isEnabled(),
|
||||
]);
|
||||
$service = new SplitTicketSyncService();
|
||||
$ticketId = trim((string) $input->getOption('ticket'));
|
||||
|
||||
if ($ticketId !== '' && ctype_digit($ticketId)) {
|
||||
$result = $service->syncOne((int) $ticketId, true);
|
||||
if (!empty($result['skipped'])) {
|
||||
$output->writeln('<comment>跳过: ' . ($result['message'] ?? '') . '</comment>');
|
||||
return;
|
||||
}
|
||||
if ($result['success']) {
|
||||
$output->writeln('<info>工单 #' . $ticketId . ' 同步成功</info>');
|
||||
} else {
|
||||
$output->writeln('<error>工单 #' . $ticketId . ' 同步失败: ' . ($result['message'] ?? '') . '</error>');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$count = $service->syncDueTickets();
|
||||
$output->writeln('<info>本次处理工单数: ' . $count . '</info>');
|
||||
if (SplitTicketSyncLogger::isEnabled()) {
|
||||
$output->writeln('<comment>调试日志已写入 runtime/log/split_sync.log</comment>');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,9 @@ class Number extends Backend
|
||||
|
||||
protected $modelSceneValidate = true;
|
||||
|
||||
/** @var string */
|
||||
protected $multiFields = 'status,manual_manage';
|
||||
|
||||
/** @var string[] */
|
||||
protected $noNeedRight = ['script'];
|
||||
|
||||
@@ -58,6 +61,7 @@ class Number extends Backend
|
||||
$this->assignconfig('numberTypeList', $this->model->getNumberTypeList());
|
||||
$this->assignconfig('statusList', $this->model->getStatusList());
|
||||
$this->assignconfig('manualManageList', $this->model->getManualManageList());
|
||||
$this->assignconfig('platformStatusList', $this->model->getPlatformStatusList());
|
||||
|
||||
$this->setupPatchFrontend();
|
||||
}
|
||||
@@ -126,6 +130,59 @@ class Number extends Backend
|
||||
return (string) $this->view->fetch($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表状态开关:手动关闭时标记 manual_manage=1,防止同步自动恢复开启
|
||||
*
|
||||
* @param string $ids
|
||||
*/
|
||||
public function multi($ids = '')
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
$this->error(__('Invalid parameters'));
|
||||
}
|
||||
$ids = $ids ?: $this->request->post('ids', '');
|
||||
if ($ids === '') {
|
||||
$this->error(__('Parameter %s can not be empty', 'ids'));
|
||||
}
|
||||
|
||||
$params = $this->request->post('params', '');
|
||||
parse_str((string) $params, $values);
|
||||
if (!isset($values['status'])) {
|
||||
parent::multi($ids);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((string) $values['status'] === 'hidden') {
|
||||
$values['manual_manage'] = 1;
|
||||
} elseif ((string) $values['status'] === 'normal') {
|
||||
$values['manual_manage'] = 0;
|
||||
}
|
||||
|
||||
$adminIds = $this->getDataLimitAdminIds();
|
||||
if (is_array($adminIds)) {
|
||||
$this->model->where($this->dataLimitField, 'in', $adminIds);
|
||||
}
|
||||
$count = 0;
|
||||
Db::startTrans();
|
||||
try {
|
||||
$list = $this->model->where($this->model->getPk(), 'in', $ids)->select();
|
||||
foreach ($list as $item) {
|
||||
$count += $item->allowField(true)->isUpdate(true)->save($values);
|
||||
}
|
||||
Db::commit();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
} catch (PDOException|Exception $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->success();
|
||||
}
|
||||
$this->error(__('No rows were updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|Json
|
||||
* @throws DbException
|
||||
|
||||
@@ -7,6 +7,9 @@ namespace app\admin\controller\split;
|
||||
use app\admin\model\split\Link as LinkModel;
|
||||
use app\admin\model\split\Ticket as TicketModel;
|
||||
use app\common\controller\Backend;
|
||||
use app\common\service\SplitTicketRuleService;
|
||||
use app\common\service\SplitTicketSyncLogger;
|
||||
use app\common\service\SplitTicketSyncService;
|
||||
use think\Db;
|
||||
use think\Lang;
|
||||
use think\Loader;
|
||||
@@ -55,6 +58,12 @@ class Ticket extends Backend
|
||||
$this->assignconfig('ticketTypeList', $this->model->getTicketTypeList());
|
||||
$this->assignconfig('numberTypeList', $this->model->getNumberTypeList());
|
||||
$this->assignconfig('statusList', $this->model->getStatusList());
|
||||
$this->assignconfig([
|
||||
'syncConfirmMsg' => __('Sync confirm'),
|
||||
'syncBackgroundStartedMsg' => __('Sync background started'),
|
||||
'syncInProgressMsg' => __('Sync in progress'),
|
||||
'syncTicketStartedMsg' => __('Sync ticket started'),
|
||||
]);
|
||||
|
||||
$this->setupPatchFrontend();
|
||||
}
|
||||
@@ -199,11 +208,127 @@ class Ticket extends Backend
|
||||
$params['sync_status'],
|
||||
$params['sync_time'],
|
||||
$params['sync_message'],
|
||||
$params['sync_fail_count'],
|
||||
$params['speed_snapshot_count'],
|
||||
$params['speed_snapshot_time'],
|
||||
$params['click_count']
|
||||
);
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动同步选中工单
|
||||
*/
|
||||
public function sync(): void
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
$this->error(__('Invalid parameters'));
|
||||
}
|
||||
$ids = $this->request->post('ids', '');
|
||||
if ($ids === '') {
|
||||
$this->error(__('Parameter %s can not be empty', 'ids'));
|
||||
}
|
||||
$adminIds = $this->getDataLimitAdminIds();
|
||||
$pk = $this->model->getPk();
|
||||
$list = $this->model->where($pk, 'in', $ids)->select();
|
||||
if (!$list || count($list) === 0) {
|
||||
$this->error(__('No Results were found'));
|
||||
}
|
||||
|
||||
SplitTicketSyncLogger::log('web', 'manual sync request', [
|
||||
'ids' => $ids,
|
||||
'appDebug' => SplitTicketSyncLogger::isEnabled(),
|
||||
]);
|
||||
|
||||
$service = new SplitTicketSyncService();
|
||||
$ok = 0;
|
||||
$fail = 0;
|
||||
$messages = [];
|
||||
|
||||
foreach ($list as $row) {
|
||||
if (is_array($adminIds) && !in_array((int) $row[$this->dataLimitField], $adminIds, true)) {
|
||||
$fail++;
|
||||
$messages[] = '#' . $row['id'] . ': 无权限';
|
||||
continue;
|
||||
}
|
||||
$result = $service->syncOne((int) $row['id'], true);
|
||||
if ($result['success']) {
|
||||
$ok++;
|
||||
} else {
|
||||
$fail++;
|
||||
$messages[] = '#' . $row['id'] . ': ' . ($result['message'] ?? '失败');
|
||||
}
|
||||
}
|
||||
|
||||
$summary = sprintf('成功 %d 条,失败 %d 条', $ok, $fail);
|
||||
if ($fail > 0) {
|
||||
$summary .= ';' . implode(';', array_slice($messages, 0, 5));
|
||||
}
|
||||
$this->success($summary);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新:工单状态变更时联动号码
|
||||
*
|
||||
* @param string $ids
|
||||
*/
|
||||
public function multi($ids = '')
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
$this->error(__('Invalid parameters'));
|
||||
}
|
||||
$ids = $ids ?: $this->request->post('ids', '');
|
||||
if ($ids === '') {
|
||||
$this->error(__('Parameter %s can not be empty', 'ids'));
|
||||
}
|
||||
|
||||
$params = $this->request->post('params', '');
|
||||
parse_str((string) $params, $values);
|
||||
$ruleService = new SplitTicketRuleService();
|
||||
|
||||
if (isset($values['status'])) {
|
||||
$pk = $this->model->getPk();
|
||||
$adminIds = $this->getDataLimitAdminIds();
|
||||
$query = $this->model->where($pk, 'in', $ids);
|
||||
if (is_array($adminIds)) {
|
||||
$query->where($this->dataLimitField, 'in', $adminIds);
|
||||
}
|
||||
$rows = $query->select();
|
||||
if (!$rows || count($rows) === 0) {
|
||||
$this->error(__('No Results were found'));
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
Db::startTrans();
|
||||
try {
|
||||
foreach ($rows as $item) {
|
||||
$count += $item->allowField(true)->isUpdate(true)->save($values);
|
||||
}
|
||||
Db::commit();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
} catch (PDOException|Exception $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$fresh = $this->model->get($row['id']);
|
||||
if ($fresh) {
|
||||
$ruleService->syncNumbersWithTicketStatus($fresh);
|
||||
}
|
||||
}
|
||||
|
||||
if ($count > 0) {
|
||||
$this->success();
|
||||
}
|
||||
$this->error(__('No rows were updated'));
|
||||
}
|
||||
|
||||
parent::multi($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws DbException
|
||||
@@ -244,7 +369,7 @@ class Ticket extends Backend
|
||||
if ($result === false) {
|
||||
$this->error(__('No rows were inserted'));
|
||||
}
|
||||
$this->success();
|
||||
$this->success('', null, ['id' => (int) $this->model->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,6 +402,7 @@ class Ticket extends Backend
|
||||
if (($params['number_type'] ?? '') !== 'custom') {
|
||||
$params['number_type_custom'] = '';
|
||||
}
|
||||
$oldStatus = (string) ($row['status'] ?? 'hidden');
|
||||
$result = false;
|
||||
Db::startTrans();
|
||||
try {
|
||||
@@ -297,6 +423,12 @@ class Ticket extends Backend
|
||||
if ($result === false) {
|
||||
$this->error(__('No rows were updated'));
|
||||
}
|
||||
if (isset($params['status']) && (string) $params['status'] !== $oldStatus) {
|
||||
$fresh = $this->model->get($row['id']);
|
||||
if ($fresh) {
|
||||
(new SplitTicketRuleService())->syncNumbersWithTicketStatus($fresh);
|
||||
}
|
||||
}
|
||||
$this->success();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ return [
|
||||
'Inbound_count' => '进线人数',
|
||||
'Status' => '状态',
|
||||
'Manual_manage' => '手动管理',
|
||||
'Platform_status' => '平台状态',
|
||||
'Platform status online' => '在线',
|
||||
'Platform status offline' => '离线',
|
||||
'Platform status unknown' => '未知',
|
||||
'Createtime' => '创建时间',
|
||||
'Section basic' => '基础信息',
|
||||
'Status normal' => '正常',
|
||||
|
||||
@@ -24,10 +24,18 @@ return [
|
||||
'Number_count' => '号码数量',
|
||||
'Number_count_detail' => '离线 %s / 封号 %s',
|
||||
'Sync_status' => '同步状态',
|
||||
'Sync_status_btn' => '同步状态',
|
||||
'Sync running' => '正在同步,请稍候…',
|
||||
'Sync confirm' => '确定同步所选工单吗?任务将在后台执行,完成后自动提示结果。',
|
||||
'Sync background started' => '同步任务已在后台执行,请稍候…',
|
||||
'Sync in progress' => '同步中',
|
||||
'Sync ticket started' => '正在同步工单',
|
||||
'Sync done' => '同步完成',
|
||||
'Sync status success' => '同步成功',
|
||||
'Sync status error' => '同步异常',
|
||||
'Sync status pending' => '待同步',
|
||||
'Sync display success' => '同步成功 / 在线人数 %s',
|
||||
'Sync display success' => '同步成功 / 在线 %s',
|
||||
'Sync display pending' => '待同步',
|
||||
'Sync display error' => '同步异常',
|
||||
'Createtime' => '创建时间',
|
||||
'Section basic' => '基础信息',
|
||||
|
||||
@@ -15,6 +15,21 @@ class Number extends Model
|
||||
{
|
||||
protected $name = 'split_number';
|
||||
|
||||
protected static function init(): void
|
||||
{
|
||||
self::beforeInsert(function (self $row): void {
|
||||
if ((int) ($row['weigh'] ?? 0) > 0) {
|
||||
return;
|
||||
}
|
||||
$linkId = (int) ($row['split_link_id'] ?? 0);
|
||||
if ($linkId <= 0) {
|
||||
return;
|
||||
}
|
||||
$maxWeigh = (int) self::where('split_link_id', $linkId)->max('weigh');
|
||||
$row->setAttr('weigh', $maxWeigh + 1);
|
||||
});
|
||||
}
|
||||
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
protected $createTime = 'createtime';
|
||||
@@ -26,6 +41,7 @@ class Number extends Model
|
||||
'link_url_text',
|
||||
'status_text',
|
||||
'manual_manage_text',
|
||||
'platform_status_text',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -65,6 +81,18 @@ class Number extends Model
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getPlatformStatusList(): array
|
||||
{
|
||||
return [
|
||||
'online' => __('Platform status online'),
|
||||
'offline' => __('Platform status offline'),
|
||||
'unknown' => __('Platform status unknown'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联分流链接
|
||||
*/
|
||||
@@ -123,6 +151,13 @@ class Number extends Model
|
||||
return $list[$key] ?? $key;
|
||||
}
|
||||
|
||||
public function getPlatformStatusTextAttr($value, $data): string
|
||||
{
|
||||
$key = (string) ($data['platform_status'] ?? 'unknown');
|
||||
$list = $this->getPlatformStatusList();
|
||||
return $list[$key] ?? $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据链接码生成完整分流 URL
|
||||
*/
|
||||
|
||||
@@ -127,7 +127,7 @@ class Ticket extends Model
|
||||
$total = (int) ($data['ticket_total'] ?? 0);
|
||||
$complete = (int) ($data['complete_count'] ?? 0);
|
||||
if ($total <= 0) {
|
||||
return '—';
|
||||
return '0%';
|
||||
}
|
||||
$percent = round($complete / $total * 100, 2);
|
||||
return $percent . '%';
|
||||
@@ -163,6 +163,9 @@ class Ticket extends Model
|
||||
if ($status === 'success') {
|
||||
return sprintf((string) __('Sync display success'), $online);
|
||||
}
|
||||
if ($status === 'pending') {
|
||||
return (string) __('Sync display pending');
|
||||
}
|
||||
return (string) __('Sync display error');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
<style>
|
||||
.split-ticket-sync-pending {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-weight: 600;
|
||||
color: #31708f;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.split-ticket-sync-pending .fa-spinner {
|
||||
color: #5bc0de;
|
||||
}
|
||||
.split-ticket-sync-pending .split-ticket-sync-label::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
text-align: left;
|
||||
animation: split-ticket-sync-dots 1.4s steps(4, end) infinite;
|
||||
}
|
||||
@keyframes split-ticket-sync-dots {
|
||||
0%, 20% { content: ''; }
|
||||
40% { content: '.'; }
|
||||
60% { content: '..'; }
|
||||
80%, 100% { content: '...'; }
|
||||
}
|
||||
</style>
|
||||
<div class="panel panel-default panel-intro">
|
||||
<div class="panel-heading">
|
||||
{:build_heading(null,FALSE)}
|
||||
@@ -11,6 +37,7 @@
|
||||
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('split.ticket/add')?'':'hide'}" title="{:__('Add')}"><i class="fa fa-plus"></i> {:__('Add')}</a>
|
||||
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('split.ticket/edit')?'':'hide'}" title="{:__('Edit')}"><i class="fa fa-pencil"></i> {:__('Edit')}</a>
|
||||
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('split.ticket/del')?'':'hide'}" title="{:__('Delete')}"><i class="fa fa-trash"></i> {:__('Delete')}</a>
|
||||
<a href="javascript:;" class="btn btn-info btn-sync btn-disabled disabled {:$auth->check('split.ticket/sync')?'':'hide'}" title="{:__('Sync_status_btn')}"><i class="fa fa-refresh"></i> {:__('Sync_status_btn')}</a>
|
||||
</div>
|
||||
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
|
||||
data-operate-edit="{:$auth->check('split.ticket/edit')}"
|
||||
|
||||
Reference in New Issue
Block a user