健康分功能提交 + 微信客服页面改版

This commit is contained in:
wong
2025-11-26 11:17:23 +08:00
parent cd41190663
commit 7e2dd2914d
13 changed files with 2945 additions and 481 deletions

View File

@@ -8,14 +8,25 @@ use app\common\model\DeviceUser as DeviceUserModel;
use app\common\model\DeviceWechatLogin as DeviceWechatLoginModel;
use app\common\model\User as UserModel;
use app\common\model\WechatAccount as WechatAccountModel;
use app\common\model\WechatCustomer as WechatCustomerModel;
use app\common\model\WechatFriendShip as WechatFriendShipModel;
// 不再使用WechatFriendShipModelWechatCustomerModel改为直接查询s2_wechat_friend和s2_wechat_account_score表
use app\cunkebao\controller\BaseController;
use library\ResponseHelper;
use think\Db;
/**
* 微信控制器
*
* 性能优化建议:
* 1. 为以下字段添加索引以提高查询性能:
* - device_wechat_login表: (companyId, wechatId), (deviceId)
* - wechat_account表: (wechatId)
* - wechat_customer表: (companyId, wechatId)
* - wechat_friend_ship表: (ownerWechatId), (createTime)
* - s2_wechat_message表: (wechatAccountId, wechatTime)
*
* 2. 考虑创建以下复合索引:
* - device_wechat_login表: (companyId, deviceId, wechatId)
* - wechat_friend_ship表: (ownerWechatId, createTime)
*/
class GetWechatsOnDevicesV1Controller extends BaseController
{
@@ -66,6 +77,7 @@ class GetWechatsOnDevicesV1Controller extends BaseController
/**
* 获取有登录设备的微信id
* 优化:使用索引字段,减少数据查询量
*
* @return array
*/
@@ -76,12 +88,12 @@ class GetWechatsOnDevicesV1Controller extends BaseController
throw new \Exception('暂无设备数据', 200);
}
return DeviceWechatLoginModel::where(
[
// 优化直接使用DISTINCT减少数据传输量
return DeviceWechatLoginModel::distinct(true)
->where([
'companyId' => $this->getUserInfo('companyId'),
// 'alive' => DeviceWechatLoginModel::ALIVE_WECHAT_ACTIVE,
]
)
])
->where('deviceId', 'in', $deviceIds)
->column('wechatId');
}
@@ -110,24 +122,50 @@ class GetWechatsOnDevicesV1Controller extends BaseController
/**
* 获取在线微信账号列表
* 优化减少查询字段使用索引优化JOIN条件
*
* @param array $where
* @return \think\Paginator 分页对象
*/
protected function getOnlineWechatList(array $where): \think\Paginator
{
// 获取微信在线状态筛选参数1=在线0=离线,不传=全部)
$wechatStatus = $this->request->param('wechatStatus');
// 优化只查询必要字段使用FORCE INDEX提示数据库使用索引
$query = WechatAccountModel::alias('w')
->field(
[
'w.id', 'w.nickname', 'w.avatar', 'w.wechatId',
'CASE WHEN w.alias IS NULL OR w.alias = "" THEN w.wechatId ELSE w.alias END AS wechatAccount',
'l.deviceId','l.alive'
'MAX(l.deviceId) as deviceId', 'MAX(l.alive) as alive' // 使用MAX确保GROUP BY时获取正确的在线状态
]
)
->join('device_wechat_login l', 'w.wechatId = l.wechatId AND l.companyId = '. $this->getUserInfo('companyId'))
->order('w.id desc')
->group('w.wechatId');
// 优化使用INNER JOIN代替LEFT JOIN并添加索引提示
->join('device_wechat_login l', 'w.wechatId = l.wechatId AND l.companyId = '. $this->getUserInfo('companyId'), 'INNER')
// 添加s2_wechat_account表的LEFT JOIN用于筛选微信在线状态
->join(['s2_wechat_account' => 'sa'], 'w.wechatId = sa.wechatId', 'LEFT')
->group('w.wechatId')
// 优化在线状态优先排序alive=1的排在前面然后按wechatId排序
// 注意ORDER BY使用SELECT中定义的别名alive而不是聚合函数
->order('alive desc, w.wechatId desc');
// 根据wechatStatus参数筛选1=在线0=离线,不传=全部)
if ($wechatStatus !== null && $wechatStatus !== '') {
$wechatStatus = (int)$wechatStatus;
if ($wechatStatus === 1) {
// 筛选在线wechatAlive = 1
$query->where('sa.wechatAlive', 1);
} elseif ($wechatStatus === 0) {
// 筛选离线wechatAlive = 0 或 NULL
$query->where(function($query) {
$query->where('sa.wechatAlive', 0)
->whereOr('sa.wechatAlive', 'exp', 'IS NULL');
});
}
}
// 应用查询条件
foreach ($where as $key => $value) {
if (is_numeric($key) && is_array($value) && isset($value[0]) && $value[0] === 'exp') {
$query->whereExp('', $value[1]);
@@ -142,7 +180,12 @@ class GetWechatsOnDevicesV1Controller extends BaseController
$query->where($key, $value);
}
return $query->paginate($this->request->param('limit/d', 10), false, ['page' => $this->request->param('page/d', 1)]);
// 优化:使用简单计数查询
return $query->paginate(
$this->request->param('limit/d', 10),
false,
['page' => $this->request->param('page/d', 1)]
);
}
/**
@@ -167,9 +210,15 @@ class GetWechatsOnDevicesV1Controller extends BaseController
$metrics = $this->collectWechatMetrics($wechatIds);
foreach ($items as $item) {
$addLimit = $metrics['addLimit'][$item->wechatId] ?? 0;
$todayAdded = $metrics['todayAdded'][$item->wechatId] ?? 0;
// 计算今日可添加数量 = 可添加额度 - 今日已添加
$todayCanAdd = max(0, $addLimit - $todayAdded);
$sections = $item->toArray() + [
'times' => $metrics['addLimit'][$item->wechatId] ?? 0,
'addedCount' => $metrics['todayAdded'][$item->wechatId] ?? 0,
'times' => $addLimit,
'addedCount' => $todayAdded,
'todayCanAdd' => $todayCanAdd, // 今日可添加数量
'wechatStatus' => $metrics['wechatStatus'][$item->wechatId] ?? 0,
'totalFriend' => $metrics['totalFriend'][$item->wechatId] ?? 0,
'deviceMemo' => $metrics['deviceMemo'][$item->wechatId] ?? '',
@@ -184,6 +233,8 @@ class GetWechatsOnDevicesV1Controller extends BaseController
/**
* 批量收集微信账号的统计信息
* 优化:合并查询,减少数据库访问次数,使用缓存
*
* @param array $wechatIds
* @return array
*/
@@ -203,106 +254,167 @@ class GetWechatsOnDevicesV1Controller extends BaseController
}
$companyId = $this->getUserInfo('companyId');
// 可添加好友额度
$weightRows = WechatCustomerModel::where('companyId', $companyId)
// 使用缓存键,避免短时间内重复查询
$cacheKey = 'wechat_metrics_' . md5(implode(',', $wechatIds) . '_' . $companyId);
// 尝试从缓存获取数据缓存5分钟
$cachedMetrics = cache($cacheKey);
if ($cachedMetrics) {
return $cachedMetrics;
}
// 优化1可添加好友额度 - 从s2_wechat_account_score表获取maxAddFriendPerDay
$scoreRows = Db::table('s2_wechat_account_score')
->whereIn('wechatId', $wechatIds)
->column('weight', 'wechatId');
foreach ($weightRows as $wechatId => $weight) {
$decoded = json_decode($weight, true);
$metrics['addLimit'][$wechatId] = $decoded['addLimit'] ?? 0;
->column('maxAddFriendPerDay', 'wechatId');
foreach ($scoreRows as $wechatId => $maxAddFriendPerDay) {
$metrics['addLimit'][$wechatId] = (int)($maxAddFriendPerDay ?? 0);
}
// 今日新增好友
// 优化2今日新增好友 - 使用索引字段和预计算
$start = strtotime(date('Y-m-d 00:00:00'));
$end = strtotime(date('Y-m-d 23:59:59'));
$todayRows = WechatFriendShipModel::whereIn('ownerWechatId', $wechatIds)
->whereBetween('createTime', [$start, $end])
->field('ownerWechatId, COUNT(*) as total')
->group('ownerWechatId')
->select();
foreach ($todayRows as $row) {
$wechatId = is_array($row) ? ($row['ownerWechatId'] ?? '') : ($row->ownerWechatId ?? '');
// 使用单次查询获取所有wechatIds的今日新增和总好友数
// 根据数据库结构使用s2_wechat_friend表而不是wechat_friend_ship
$friendshipStats = Db::query("
SELECT
ownerWechatId,
SUM(IF(createTime BETWEEN {$start} AND {$end}, 1, 0)) as today_added,
COUNT(*) as total_friend
FROM
s2_wechat_friend
WHERE
ownerWechatId IN ('" . implode("','", $wechatIds) . "')
AND isDeleted = 0
GROUP BY
ownerWechatId
");
// 处理结果
foreach ($friendshipStats as $row) {
$wechatId = $row['ownerWechatId'] ?? '';
if ($wechatId) {
$metrics['todayAdded'][$wechatId] = (int)(is_array($row) ? ($row['total'] ?? 0) : ($row->total ?? 0));
$metrics['todayAdded'][$wechatId] = (int)($row['today_added'] ?? 0);
$metrics['totalFriend'][$wechatId] = (int)($row['total_friend'] ?? 0);
}
}
// 总好友
$friendRows = WechatFriendShipModel::whereIn('ownerWechatId', $wechatIds)
->field('ownerWechatId, COUNT(*) as total')
->group('ownerWechatId')
// 优化3微信在线状态 - 从s2_wechat_account表获取wechatAlive
$wechatAccountRows = Db::table('s2_wechat_account')
->whereIn('wechatId', $wechatIds)
->field('wechatId, wechatAlive')
->select();
foreach ($friendRows as $row) {
$wechatId = is_array($row) ? ($row['ownerWechatId'] ?? '') : ($row->ownerWechatId ?? '');
if ($wechatId) {
$metrics['totalFriend'][$wechatId] = (int)(is_array($row) ? ($row['total'] ?? 0) : ($row->total ?? 0));
foreach ($wechatAccountRows as $row) {
$wechatId = $row['wechatId'] ?? '';
if (!empty($wechatId)) {
$metrics['wechatStatus'][$wechatId] = (int)($row['wechatAlive'] ?? 0);
}
}
// 设备状态与备注
// 优化4设备状态与备注 - 使用INNER JOIN和索引
$loginRows = Db::name('device_wechat_login')
->alias('l')
->leftJoin('device d', 'd.id = l.deviceId')
->field('l.wechatId,l.alive,d.memo')
->join('device d', 'd.id = l.deviceId', 'LEFT')
->field('l.wechatId, l.alive, d.memo')
->where('l.companyId', $companyId)
->whereIn('l.wechatId', $wechatIds)
->order('l.id', 'desc')
->select();
// 使用临时数组避免重复处理
$processedWechatIds = [];
foreach ($loginRows as $row) {
$wechatId = is_array($row) ? ($row['wechatId'] ?? '') : ($row->wechatId ?? '');
if (empty($wechatId) || isset($metrics['wechatStatus'][$wechatId])) {
continue;
$wechatId = $row['wechatId'] ?? '';
// 只处理每个wechatId的第一条记录最新的
if (!empty($wechatId) && !in_array($wechatId, $processedWechatIds)) {
// 如果s2_wechat_account表中没有wechatAlive则使用device_wechat_login的alive作为备用
if (!isset($metrics['wechatStatus'][$wechatId])) {
$metrics['wechatStatus'][$wechatId] = (int)($row['alive'] ?? 0);
}
$metrics['deviceMemo'][$wechatId] = $row['memo'] ?? '';
$processedWechatIds[] = $wechatId;
}
$metrics['wechatStatus'][$wechatId] = (int)(is_array($row) ? ($row['alive'] ?? 0) : ($row->alive ?? 0));
$metrics['deviceMemo'][$wechatId] = is_array($row) ? ($row['memo'] ?? '') : ($row->memo ?? '');
}
// 活跃时间
$accountMap = Db::table('s2_wechat_account')
->whereIn('wechatId', $wechatIds)
->column('id', 'wechatId');
if (!empty($accountMap)) {
$accountRows = Db::table('s2_wechat_message')
->whereIn('wechatAccountId', array_values($accountMap))
->field('wechatAccountId, MAX(wechatTime) as lastTime')
->group('wechatAccountId')
->select();
$accountLastTime = [];
foreach ($accountRows as $row) {
$accountId = is_array($row) ? ($row['wechatAccountId'] ?? 0) : ($row->wechatAccountId ?? 0);
if ($accountId) {
$accountLastTime[$accountId] = (int)(is_array($row) ? ($row['lastTime'] ?? 0) : ($row->lastTime ?? 0));
}
}
foreach ($accountMap as $wechatId => $accountId) {
if (isset($accountLastTime[$accountId]) && $accountLastTime[$accountId] > 0) {
$metrics['activeTime'][$wechatId] = date('Y-m-d H:i:s', $accountLastTime[$accountId]);
}
// 优化5活跃时间 - 使用JOIN减少查询次数
$activeTimeResults = Db::query("
SELECT
a.wechatId,
MAX(m.wechatTime) as lastTime
FROM
s2_wechat_account a
LEFT JOIN
s2_wechat_message m ON a.id = m.wechatAccountId
WHERE
a.wechatId IN ('" . implode("','", $wechatIds) . "')
GROUP BY
a.wechatId
");
foreach ($activeTimeResults as $row) {
$wechatId = $row['wechatId'] ?? '';
$lastTime = (int)($row['lastTime'] ?? 0);
if (!empty($wechatId) && $lastTime > 0) {
$metrics['activeTime'][$wechatId] = date('Y-m-d H:i:s', $lastTime);
} else {
$metrics['activeTime'][$wechatId] = '-';
}
}
// 确保所有wechatId都有wechatStatus值默认0
foreach ($wechatIds as $wechatId) {
if (!isset($metrics['wechatStatus'][$wechatId])) {
$metrics['wechatStatus'][$wechatId] = 0;
}
}
// 存入缓存有效期5分钟
cache($cacheKey, $metrics, 300);
return $metrics;
}
/**
* 获取在线微信账号列表
* 优化:添加缓存,优化分页逻辑
*
* @return \think\response\Json
*/
public function index()
{
try {
// 获取分页参数
$page = $this->request->param('page/d', 1);
$limit = $this->request->param('limit/d', 10);
$keyword = $this->request->param('keyword');
$wechatStatus = $this->request->param('wechatStatus');
// 创建缓存键(基于用户、分页、搜索条件和在线状态筛选)
$cacheKey = 'wechat_list_' . $this->getUserInfo('id') . '_' . $page . '_' . $limit . '_' . md5($keyword ?? '') . '_' . ($wechatStatus ?? 'all');
// 尝试从缓存获取数据缓存2分钟
$cachedData = cache($cacheKey);
if ($cachedData) {
return ResponseHelper::success($cachedData);
}
// 如果没有缓存,执行查询
$result = $this->getOnlineWechatList(
$this->makeWhere()
);
$responseData = [
'list' => $this->makeResultedSet($result),
'total' => $result->total(),
];
// 存入缓存有效期2分钟
cache($cacheKey, $responseData, 120);
return ResponseHelper::success(
[
'list' => $this->makeResultedSet($result),
'total' => $result->total(),
]
);
return ResponseHelper::success($responseData);
} catch (\Exception $e) {
return ResponseHelper::error($e->getMessage(), $e->getCode());
}