Files
links/application/admin/controller/split/Number.php
T

436 lines
15 KiB
PHP
Raw 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.
<?php
declare(strict_types=1);
namespace app\admin\controller\split;
use app\admin\model\split\Link as LinkModel;
use app\admin\model\split\Number as NumberModel;
use app\common\controller\Backend;
use think\Db;
use think\Exception;
use think\exception\DbException;
use think\exception\PDOException;
use think\exception\ValidateException;
use think\response\Json;
/**
* 号码管理
*
* @icon fa fa-phone
* @remark 分流号码管理,关联分流链接
*/
class Number extends Backend
{
/** @var NumberModel */
protected $model = null;
protected $searchFields = 'ticket_name,number';
protected $dataLimit = 'personal';
protected $modelValidate = true;
protected $modelSceneValidate = true;
/** @var string */
protected $multiFields = 'status,manual_manage';
/** @var string[] */
protected $noNeedRight = ['script'];
/** @var string */
private const PATCH_VIEW_DIR = 'patches/application/admin/view/split/number/';
public function _initialize()
{
parent::_initialize();
$lang = $this->request->langset();
$lang = preg_match('/^([a-zA-Z\-_]{2,10})$/i', $lang) ? $lang : 'zh-cn';
$langFile = ROOT_PATH . 'patches/application/admin/lang/' . $lang . '/split/number.php';
if (is_file($langFile)) {
\think\Lang::load($langFile);
}
$this->model = new NumberModel();
$this->view->assign('numberTypeList', $this->model->getNumberTypeList());
$this->view->assign('statusList', $this->model->getStatusList());
$this->view->assign('manualManageList', $this->model->getManualManageList());
$this->view->assign('splitLinkList', $this->buildSplitLinkList());
$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();
}
private function setupPatchFrontend(): void
{
$patchJs = ROOT_PATH . 'patches/public/assets/js/backend/split/number.js';
$publicJs = ROOT_PATH . 'public/assets/js/backend/split/number.js';
$usePatchJs = is_file($patchJs) && (
!is_file($publicJs) || filemtime($patchJs) >= filemtime($publicJs)
);
if (!$usePatchJs) {
return;
}
$cfg = is_array($this->view->config ?? null) ? $this->view->config : [];
$version = (string) \think\Config::get('site.version');
// 使用站内相对路径,避免生产环境 domain() 与反向代理/HTTPS 不一致导致 JS 跨域丢 Cookie
$scriptUrl = (string) url('split.number/script', ['v' => $version], '', false);
$cfg['jsname'] = $scriptUrl;
$this->view->assign('config', $cfg);
$this->view->config = $cfg;
}
/**
* @return array<int, array<string, string>>
*/
private function buildSplitLinkList(): array
{
$query = (new LinkModel())->where('status', 'normal')->order('id', 'desc');
if ($this->dataLimit) {
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
$query->where('admin_id', 'in', $adminIds);
}
}
$list = [];
foreach ($query->select() as $row) {
$code = (string) $row['link_code'];
$desc = (string) $row['description'];
$label = $desc !== '' ? $code . ' - ' . $desc : $code;
$list[] = [
'id' => (string) $row['id'],
'label' => $label,
];
}
return $list;
}
private function fetchPatch(string $template): string
{
$patchFile = ROOT_PATH . self::PATCH_VIEW_DIR . $template . '.html';
$appFile = APP_PATH . 'admin/view/split/number/' . $template . '.html';
if (is_file($patchFile)) {
$file = $patchFile;
} elseif (is_file($appFile)) {
$file = $appFile;
} else {
$this->error('模板文件不存在');
}
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
*/
public function index()
{
$this->request->filter(['strip_tags', 'trim']);
if (false === $this->request->isAjax()) {
return $this->fetchPatch('index');
}
if ($this->request->request('keyField')) {
return $this->selectpage();
}
[$where, $sort, $order, $offset, $limit] = $this->buildparams();
$list = $this->model
->with(['splitLink'])
->where($where)
->order($sort, $order)
->paginate($limit);
$result = ['total' => $list->total(), 'rows' => $list->items()];
return json($result);
}
/**
* @return string
* @throws DbException
*/
public function add()
{
if (false === $this->request->isPost()) {
return $this->fetchPatch('add');
}
$params = $this->request->post('row/a', []);
if ($params === []) {
$this->error(__('Parameter %s can not be empty', ''));
}
$params = $this->preExcludeFields($params);
if (($params['number_type'] ?? '') !== 'custom') {
$params['number_type_custom'] = '';
}
$numbersText = (string) ($params['numbers'] ?? '');
unset($params['numbers']);
$numberList = NumberModel::parseNumbersText($numbersText);
if ($numberList === []) {
$this->error(__('Please fill at least one number'));
}
$validateData = array_merge($params, ['numbers' => $numbersText]);
$adminId = $this->dataLimit && $this->dataLimitFieldAutoFill ? (int) $this->auth->id : 0;
$inserted = 0;
$skipped = 0;
Db::startTrans();
try {
if ($this->modelValidate) {
$name = str_replace('\\model\\', '\\validate\\', get_class($this->model));
$validate = $this->modelSceneValidate ? $name . '.add' : $name;
$this->model->validateFailException()->validate($validate, $validateData);
}
$splitLinkId = (int) ($params['split_link_id'] ?? 0);
$baseRow = [
'split_link_id' => $splitLinkId,
'ticket_name' => (string) ($params['ticket_name'] ?? ''),
'number_type' => (string) ($params['number_type'] ?? ''),
'number_type_custom' => (string) ($params['number_type_custom'] ?? ''),
'status' => (string) ($params['status'] ?? 'normal'),
'visit_count' => 0,
'inbound_count' => 0,
'manual_manage' => 0,
];
if ($adminId > 0) {
$baseRow['admin_id'] = $adminId;
}
foreach ($numberList as $num) {
$exists = $this->model
->where('split_link_id', $splitLinkId)
->where('number', $num)
->find();
if ($exists) {
$skipped++;
continue;
}
$row = $baseRow;
$row['number'] = $num;
$item = new NumberModel();
$item->allowField(true)->isUpdate(false)->save($row);
$inserted++;
}
if ($inserted === 0) {
throw new Exception(__('All numbers already exist for this link'));
}
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (PDOException|Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
$msg = __('Inserted %d number(s)', $inserted);
if ($skipped > 0) {
$msg .= '' . __('Skipped %d duplicate(s)', $skipped);
}
$this->success($msg);
}
/**
* @param string|null $ids
* @return string
* @throws DbException
*/
public function edit($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds) && !in_array((int) $row[$this->dataLimitField], $adminIds, true)) {
$this->error(__('You have no permission'));
}
if (false === $this->request->isPost()) {
$this->view->assign('row', $row);
return $this->fetchPatch('edit');
}
$params = $this->request->post('row/a', []);
if ($params === []) {
$this->error(__('Parameter %s can not be empty', ''));
}
$params = $this->preExcludeFields($params);
if (($params['number_type'] ?? '') !== 'custom') {
$params['number_type_custom'] = '';
}
$newNumber = trim((string) ($params['number'] ?? ''));
$splitLinkId = (int) ($params['split_link_id'] ?? $row['split_link_id']);
$exists = $this->model
->where('split_link_id', $splitLinkId)
->where('number', $newNumber)
->where('id', '<>', (int) $row['id'])
->find();
if ($exists) {
$this->error(__('Number already exists for this link'));
}
$result = false;
Db::startTrans();
try {
if ($this->modelValidate) {
$name = str_replace('\\model\\', '\\validate\\', get_class($this->model));
$validate = $this->modelSceneValidate ? $name . '.edit' : $name;
$row->validateFailException()->validate($validate, $params);
}
$result = $row->allowField(true)->save($params);
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (PDOException|Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result === false) {
$this->error(__('No rows were updated'));
}
$this->success();
}
/**
* 批量更新号码状态与手动管理
*/
public function batchupdate(): void
{
if (false === $this->request->isPost()) {
$this->error(__('Invalid parameters'));
}
$ids = $this->request->post('ids', '');
if ($ids === '' || $ids === null) {
$this->error(__('Parameter %s can not be empty', 'ids'));
}
if (!is_array($ids)) {
$ids = explode(',', (string) $ids);
}
$ids = array_filter(array_map('intval', $ids));
if ($ids === []) {
$this->error(__('Parameter %s can not be empty', 'ids'));
}
$status = (string) $this->request->post('status', '');
$manualManage = $this->request->post('manual_manage', '');
$statusList = $this->model->getStatusList();
if (!isset($statusList[$status])) {
$this->error(__('Invalid status'));
}
if (!in_array((string) $manualManage, ['0', '1'], true)) {
$this->error(__('Invalid manual manage'));
}
$query = $this->model->where('id', 'in', $ids);
if ($this->dataLimit) {
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
$query->where('admin_id', 'in', $adminIds);
}
}
$count = $query->update([
'status' => $status,
'manual_manage' => (int) $manualManage,
]);
if ($count === false || $count === 0) {
$this->error(__('No rows were updated'));
}
$this->success(__('Batch update success'));
}
/**
* 排除不可由表单提交的字段
*
* @param array<string, mixed> $params
* @return array<string, mixed>
*/
protected function preExcludeFields($params): array
{
$params = parent::preExcludeFields($params);
unset(
$params['visit_count'],
$params['inbound_count'],
$params['manual_manage'],
$params['id']
);
return $params;
}
public function script(): void
{
$jsFile = ROOT_PATH . 'patches/public/assets/js/backend/split/number.js';
if (!is_file($jsFile)) {
$jsFile = ROOT_PATH . 'public/assets/js/backend/split/number.js';
}
if (!is_file($jsFile)) {
$this->error('脚本文件不存在');
}
$content = file_get_contents($jsFile);
if ($content === false) {
$this->error('读取脚本失败');
}
$response = response($content, 200, [
'Content-Type' => 'application/javascript; charset=utf-8',
'Cache-Control' => 'public, max-age=3600',
]);
throw new \think\exception\HttpResponseException($response);
}
}