客服线路
This commit is contained in:
parent
1142d443b9
commit
1165277f87
|
@ -84,7 +84,7 @@
|
||||||
width="160"
|
width="160"
|
||||||
prop="mobile"
|
prop="mobile"
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column width="138px" align="center" label="路线">
|
<!-- <el-table-column width="138px" align="center" label="路线">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag v-if="scope.row.route_type === 10" type="border-card"
|
<el-tag v-if="scope.row.route_type === 10" type="border-card"
|
||||||
>境内路线</el-tag
|
>境内路线</el-tag
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
>境外路线</el-tag
|
>境外路线</el-tag
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column> -->
|
||||||
|
|
||||||
<!-- <el-table-column align="center" label="头像">
|
<!-- <el-table-column align="center" label="头像">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
@ -244,12 +244,12 @@
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="路线">
|
<!-- <el-form-item label="路线">
|
||||||
<el-radio-group v-model="item.route_type">
|
<el-radio-group v-model="item.route_type">
|
||||||
<el-radio :label="10">境内跟团</el-radio>
|
<el-radio :label="10">境内跟团</el-radio>
|
||||||
<el-radio :label="20">境外跟团</el-radio>
|
<el-radio :label="20">境外跟团</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item> -->
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button type="primary" @click="onSave(item)">保 存</el-button>
|
<el-button type="primary" @click="onSave(item)">保 存</el-button>
|
||||||
|
|
|
@ -40,12 +40,11 @@
|
||||||
<span>{{ scope.row.last_work_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
<span>{{ scope.row.last_work_time | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column width="138px" align="center" label="路线">
|
<!-- <el-table-column width="138px" align="center" label="线路">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag v-if="scope.row.route_type === 10" type="border-card">境内路线</el-tag>
|
<el-tag type="border-card">{{ scope.row.routes }}</el-tag>
|
||||||
<el-tag v-if="scope.row.route_type === 20" type="success">境外路线</el-tag>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column> -->
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -38,8 +38,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
proxy: {
|
proxy: {
|
||||||
'/dev-api': { // 接口地址 以 api开头的都走下面的配置
|
'/dev-api': { // 接口地址 以 api开头的都走下面的配置
|
||||||
// target: 'https://www.szjinao.cn', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
|
target: 'https://www.szjinao.cn', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
|
||||||
target: 'http://192.168.0.100:8787', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
|
// target: 'http://192.168.0.100:8787', // 代理目标地址为后端服务器地址 127.0.0.1 192.168.1.2
|
||||||
ws: true, // 是否支持 websocket 请求 支持
|
ws: true, // 是否支持 websocket 请求 支持
|
||||||
changeOrigin: true, // 是否启用跨域
|
changeOrigin: true, // 是否启用跨域
|
||||||
pathRewrite: {
|
pathRewrite: {
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace app\admin\controller;
|
||||||
use app\model\Admins;
|
use app\model\Admins;
|
||||||
use app\model\Onlines;
|
use app\model\Onlines;
|
||||||
use app\model\Orders;
|
use app\model\Orders;
|
||||||
|
use app\model\Products;
|
||||||
use support\Log;
|
use support\Log;
|
||||||
use support\Request;
|
use support\Request;
|
||||||
use support\Redis;
|
use support\Redis;
|
||||||
|
@ -184,6 +185,10 @@ class AdminController extends base
|
||||||
$list[$adminId]['id'] = $adminId;
|
$list[$adminId]['id'] = $adminId;
|
||||||
$list[$adminId]['username'] = $user->username;
|
$list[$adminId]['username'] = $user->username;
|
||||||
$list[$adminId]['route_type'] = $user->route_type;
|
$list[$adminId]['route_type'] = $user->route_type;
|
||||||
|
$list[$adminId]['routes'] = [];
|
||||||
|
if ($user->product_ids) {
|
||||||
|
$list[$adminId]['routes'] = Products::query()->field(['product_name'])->whereIn('id', explode(',', $user->product_ids))->select();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->success(array_values($list));
|
return $this->success(array_values($list));
|
||||||
|
|
|
@ -4,10 +4,12 @@ require_once(__DIR__.'/xlsxwriter.class.php');
|
||||||
|
|
||||||
use app\model\Admins;
|
use app\model\Admins;
|
||||||
use app\model\Backs;
|
use app\model\Backs;
|
||||||
|
use app\model\Blacks;
|
||||||
use app\model\Follows;
|
use app\model\Follows;
|
||||||
use app\model\Orders;
|
use app\model\Orders;
|
||||||
use app\model\Logs;
|
use app\model\Logs;
|
||||||
use app\model\ThirdMobileLogs;
|
use app\model\ThirdMobileLogs;
|
||||||
|
use app\server\SMS;
|
||||||
use app\server\ThirdApiService;
|
use app\server\ThirdApiService;
|
||||||
use app\server\Orders as ServerOrders;
|
use app\server\Orders as ServerOrders;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
@ -389,6 +391,40 @@ class OrderController extends base
|
||||||
return $this->success($backs->append(['order_status_name','os_name']),null, Orders::OSS);
|
return $this->success($backs->append(['order_status_name','os_name']),null, Orders::OSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量流转出
|
||||||
|
public function backBatch(Request $request) {
|
||||||
|
$sn = $request->post('sn');
|
||||||
|
if (empty($sn) || !is_array($sn)) {
|
||||||
|
return $this->error(2001, '请选择流转的订单');
|
||||||
|
}
|
||||||
|
$toAdminId = $request->post('to_admin_id');
|
||||||
|
if (!$toAdminId) {
|
||||||
|
return $this->error(2001, '请选择流转的客服');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orders::whereIn('sn', $sn)->update(['admin_id'=> $toAdminId]);
|
||||||
|
|
||||||
|
foreach ($sn as $value) {
|
||||||
|
$item = Orders::where('sn', $value)->find();
|
||||||
|
if($item->admin_id == $toAdminId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Backs::create([
|
||||||
|
'order_id' => $item->id,
|
||||||
|
'admin_id' => $toAdminId,
|
||||||
|
'admin' => $item->admin_id,
|
||||||
|
'apply_id' => $request->admin->id,
|
||||||
|
'status' => 1
|
||||||
|
]);
|
||||||
|
Orders::where('sn', $value)->update(['admin_id'=> $toAdminId]);
|
||||||
|
// 发送短信
|
||||||
|
\app\server\Orders::sendOrderSms($toAdminId, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success(true);
|
||||||
|
}
|
||||||
|
|
||||||
//把订单拉回
|
//把订单拉回
|
||||||
public function back(Request $request) {
|
public function back(Request $request) {
|
||||||
$sn = $request->post('sn');
|
$sn = $request->post('sn');
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
namespace app\admin\controller;
|
||||||
|
|
||||||
|
use app\model\Admins;
|
||||||
|
use app\model\Onlines;
|
||||||
|
use app\model\Orders;
|
||||||
|
use app\model\Products;
|
||||||
|
use support\Log;
|
||||||
|
use support\Request;
|
||||||
|
use support\Redis;
|
||||||
|
|
||||||
|
class ProductsController extends base {
|
||||||
|
/**
|
||||||
|
* 线路列表
|
||||||
|
* @param Request $request
|
||||||
|
* @return \support\Response
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
*/
|
||||||
|
public function list(Request $request) {
|
||||||
|
$query = Products::where('status', 1)->order('id', 'desc');
|
||||||
|
if($username = $request->get('username')) {
|
||||||
|
$query->where('username', $username);
|
||||||
|
}
|
||||||
|
if($status = $request->get('status')) {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
|
if($is_order = $request->get('is_order')) {
|
||||||
|
$query->where('is_order', $is_order);
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = $query->paginate($request->get('limit',1000));
|
||||||
|
return $this->success($list,null,['oss' => array_values(array_map(function ($os, $k) {
|
||||||
|
return ['id' => $k, 'os' => $os];
|
||||||
|
}, Orders::OSS, array_keys(Orders::OSS)))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add(Request $request) {
|
||||||
|
if (!$request->post('os')) {
|
||||||
|
return $this->error(2001, '请选择平台.');
|
||||||
|
}
|
||||||
|
if (!$request->post('product_name')) {
|
||||||
|
return $this->error(2001, '线路名称必填');
|
||||||
|
}
|
||||||
|
if (!$request->post('third_product_id')) {
|
||||||
|
return $this->error(2001, '线路id必填');
|
||||||
|
}
|
||||||
|
|
||||||
|
$where = ['os' => $request->get('os'), 'third_product_id' => $request->get('third_product_id')];
|
||||||
|
$product = (new Products())->where($where)->find();
|
||||||
|
if (!empty($product)) {
|
||||||
|
return $this->error(2002, '线路已存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
$product = new Products();
|
||||||
|
$product->os = $request->post('os');
|
||||||
|
$product->third_product_id = $request->post('third_product_id');
|
||||||
|
$product->product_name = $request->post('product_name');
|
||||||
|
$product->status = 1;
|
||||||
|
$product->save();
|
||||||
|
Log::info('product:' . json_encode($product));
|
||||||
|
return $this->success([]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
class Products extends base {
|
||||||
|
}
|
|
@ -3,15 +3,19 @@
|
||||||
namespace app\server;
|
namespace app\server;
|
||||||
|
|
||||||
use app\common\Error;
|
use app\common\Error;
|
||||||
|
use app\model\Admins;
|
||||||
use app\model\Admins as AdminsModel;
|
use app\model\Admins as AdminsModel;
|
||||||
|
use app\model\Blacks;
|
||||||
use app\model\FilterMobiles;
|
use app\model\FilterMobiles;
|
||||||
use app\model\Finances as FinancesModel;
|
use app\model\Finances as FinancesModel;
|
||||||
use app\model\LiveRoomWorks;
|
use app\model\LiveRoomWorks;
|
||||||
use app\model\Orders as OrdersModel;
|
use app\model\Orders as OrdersModel;
|
||||||
|
use app\model\Products;
|
||||||
use support\Log;
|
use support\Log;
|
||||||
use support\Redis;
|
use support\Redis;
|
||||||
|
|
||||||
class Orders {
|
class Orders {
|
||||||
|
protected static $redisPool = [];
|
||||||
public static function isDaishiyong(OrdersModel $order): bool
|
public static function isDaishiyong(OrdersModel $order): bool
|
||||||
{
|
{
|
||||||
// 根据 OrdersModel::AllOssStatusSql[1] 进行判断
|
// 根据 OrdersModel::AllOssStatusSql[1] 进行判断
|
||||||
|
@ -237,4 +241,116 @@ class Orders {
|
||||||
|
|
||||||
return $roomWork;
|
return $roomWork;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分配用户
|
||||||
|
* @param int $status
|
||||||
|
* @param string $categoryDesc
|
||||||
|
* @return int|string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function poolUser($status, $thirdProductId) {
|
||||||
|
$status .= $thirdProductId;
|
||||||
|
if (empty(self::$redisPool[$status])) {
|
||||||
|
self::$redisPool[$status] = Redis::hGetAll('CRM:Pool:' . $status);
|
||||||
|
$users = self::users($thirdProductId);
|
||||||
|
$_users = [];
|
||||||
|
if (empty(self::$redisPool[$status])) {
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$_users[$user->username] = 0;
|
||||||
|
Redis::hSet('CRM:Pool:' . $status, $user->username, 0);
|
||||||
|
}
|
||||||
|
self::$redisPool[$status] = $_users;
|
||||||
|
} else {
|
||||||
|
asort(self::$redisPool[$status]);
|
||||||
|
$key_users = array_keys(self::$redisPool[$status]);
|
||||||
|
$username = $key_users[0];
|
||||||
|
$max = self::$redisPool[$status][$username];
|
||||||
|
$_users = [];
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$_users[] = $user->username;
|
||||||
|
if (!in_array($user->username, $key_users)) {
|
||||||
|
self::$redisPool[$status][$username] = $max;
|
||||||
|
Redis::hSet('CRM:Pool:' . $status, $user->username, $max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (self::$redisPool[$status] as $username => $val) {
|
||||||
|
if (!in_array($username, $_users)) {
|
||||||
|
unset(self::$redisPool[$status][$username]);
|
||||||
|
Redis::hDel('CRM:Pool:' . $status, $username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = null;
|
||||||
|
try {
|
||||||
|
$pool = self::$redisPool[$status];
|
||||||
|
if (empty($pool)) $pool = [];
|
||||||
|
asort($pool);
|
||||||
|
$keys = array_keys($pool);
|
||||||
|
if (empty($keys)) {
|
||||||
|
throw new \Exception('没有可以分配的用户');
|
||||||
|
}
|
||||||
|
$username = $keys[0];
|
||||||
|
self::$redisPool[$status][$username] += 1;
|
||||||
|
Redis::hIncrBy('CRM:Pool:' . $status, $username, 1);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error(dirname(__FILE__) . __LINE__ . '没有可以分配的用户', func_get_args());
|
||||||
|
throw new \Exception('没有可以分配的用户');
|
||||||
|
}
|
||||||
|
return $username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $thirdProductId
|
||||||
|
* @return array
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
protected static function users($thirdProductId) {
|
||||||
|
$product = Products::query()->where('third_product_id', $thirdProductId)->find();
|
||||||
|
if (empty($product)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$users = Admins::where('status', 1)->where('is_order', 1)->whereFindInSet('product_ids', $product->id)->select();
|
||||||
|
if ($thirdProductId == '1128292503') {
|
||||||
|
Log::info(' 1128292503 find not one...' . json_encode($product));
|
||||||
|
}
|
||||||
|
$us = [];
|
||||||
|
foreach ($users as $u) {
|
||||||
|
$ru = Redis::get('CRM:USER:ONLINE:' . $u->id);
|
||||||
|
if (empty($ru)) continue;
|
||||||
|
|
||||||
|
$_u = new \stdClass();
|
||||||
|
$_u->username = $u->id;
|
||||||
|
$us[] = $_u;
|
||||||
|
};
|
||||||
|
|
||||||
|
return $us;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送订单短信
|
||||||
|
* @param $admin_id
|
||||||
|
* @param $order
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public static function sendOrderSms($admin_id, $order)
|
||||||
|
{
|
||||||
|
$user = Admins::cache(true)->where('id', $admin_id)->find();
|
||||||
|
if ((!config('app.debug', true) || config('app.debug', true) === 'false') && (time() * 1000 - $order->create_at) / 1000 < 2 * 24 * 3600) {
|
||||||
|
$has = Blacks::where('mobile', $order->mobile)->find();
|
||||||
|
if (empty($has) && !empty($order->mobile)) {
|
||||||
|
SMS::juhe_sms_send($order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]);
|
||||||
|
} else {
|
||||||
|
sleep(10);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log::info('订单未发送短息:' . json_encode([$order->mobile, 261607, ['title' => $order->product_name, 'mobile' => $user->mobile]]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
||||||
.pagination-container[data-v-6af373ef]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-6af373ef]{display:none}
|
|
|
@ -0,0 +1 @@
|
||||||
|
.app-container[data-v-7aa35cbf]{position:relative;padding-bottom:60px}.el-table[data-v-7aa35cbf],.filter-container[data-v-7aa35cbf]{padding-bottom:52px}.search[data-v-7aa35cbf]{margin-left:10px}
|
|
@ -1 +0,0 @@
|
||||||
.app-container[data-v-7c77c70f]{position:relative;padding-bottom:60px}.el-table[data-v-7c77c70f],.filter-container[data-v-7c77c70f]{padding-bottom:52px}.search[data-v-7c77c70f]{margin-left:10px}
|
|
|
@ -1 +1 @@
|
||||||
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-414c5a51]{position:relative;padding-bottom:60px}.el-table[data-v-414c5a51],.filter-container[data-v-414c5a51]{padding-bottom:5px}
|
.pagination-container[data-v-28fdfbeb]{padding:32px 16px;position:fixed;bottom:0;left:0;width:100%;background:#fff;padding:40px 280px;-webkit-box-shadow:0 -2px 10px rgba(0,0,0,.1);box-shadow:0 -2px 10px rgba(0,0,0,.1);z-index:100}.pagination-container.hidden[data-v-28fdfbeb]{display:none}.app-container[data-v-4d8f2a06]{position:relative;padding-bottom:60px}.el-table[data-v-4d8f2a06],.filter-container[data-v-4d8f2a06]{padding-bottom:5px}
|
|
@ -0,0 +1 @@
|
||||||
|
.pagination-container[data-v-6af373ef]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-6af373ef]{display:none}[data-v-ea07b72c].el-select{width:100%}.addRoutes[data-v-ea07b72c]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:end}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue