客服评分体系优化

This commit is contained in:
wong
2025-11-21 17:22:33 +08:00
parent ccf947c64d
commit 36eb67a217
2 changed files with 711 additions and 157 deletions

View File

@@ -4,6 +4,8 @@ namespace app\common\service;
use think\Db;
use think\Exception;
use think\facade\Log;
use think\facade\Cache;
/**
* 微信账号健康分评分服务(优化版)
@@ -14,33 +16,63 @@ use think\Exception;
* 2. 各个评分维度独立存储
* 3. 使用独立的评分记录表
* 4. 好友数量评分特殊处理(避免同步问题)
* 5. 动态分仅统计近30天数据
* 6. 优化数据库查询,减少重复计算
* 7. 添加完善的日志记录,便于问题排查
*
* 健康分 = 基础分 + 动态分
* 基础分60-100分默认60分 + 基础信息10分 + 好友数量30分
* 动态分:扣分和加分规则
*
* @author Your Name
* @version 2.0.0
*/
class WechatAccountHealthScoreService
{
// 默认基础分
/**
* 缓存相关配置
*/
const CACHE_PREFIX = 'wechat_health_score:'; // 缓存前缀
const CACHE_TTL = 3600; // 缓存有效期(秒)
/**
* 默认基础分
*/
const DEFAULT_BASE_SCORE = 60;
// 基础信息分数
/**
* 基础信息分数
*/
const BASE_INFO_SCORE = 10;
// 好友数量分数区间
const FRIEND_COUNT_SCORE_0_50 = 3;
const FRIEND_COUNT_SCORE_51_500 = 6;
const FRIEND_COUNT_SCORE_501_3000 = 8;
const FRIEND_COUNT_SCORE_3001_PLUS = 12;
/**
* 好友数量分数区间
*/
const FRIEND_COUNT_SCORE_0_50 = 3; // 0-50个好友
const FRIEND_COUNT_SCORE_51_500 = 6; // 51-500个好友
const FRIEND_COUNT_SCORE_501_3000 = 8; // 501-3000个好友
const FRIEND_COUNT_SCORE_3001_PLUS = 12; // 3001+个好友
// 动态分扣分规则
const PENALTY_FIRST_FREQUENT = -15; // 首次频繁扣15分
/**
* 动态分扣分规则
*/
const PENALTY_FIRST_FREQUENT = -15; // 首次频繁扣15分
const PENALTY_SECOND_FREQUENT = -25; // 再次频繁扣25分
const PENALTY_BANNED = -60; // 封号扣60分
// 动态分加分规则
/**
* 动态分加分规则
*/
const BONUS_NO_FREQUENT_PER_DAY = 5; // 连续3天不触发频繁每天+5分
/**
* 数据库表名
*/
const TABLE_WECHAT_ACCOUNT = 's2_wechat_account';
const TABLE_WECHAT_ACCOUNT_SCORE = 's2_wechat_account_score';
const TABLE_FRIEND_TASK = 's2_friend_task';
const TABLE_WECHAT_MESSAGE = 's2_wechat_message';
/**
* 计算并更新账号健康分
*
@@ -48,38 +80,65 @@ class WechatAccountHealthScoreService
* @param array $accountData 账号数据(可选,如果不传则从数据库查询)
* @param bool $forceRecalculateBase 是否强制重新计算基础分默认false
* @return array 返回评分结果
* @throws Exception 如果计算过程中出现错误
*/
public function calculateAndUpdate($accountId, $accountData = null, $forceRecalculateBase = false)
{
// 参数验证
if (empty($accountId) || !is_numeric($accountId)) {
$errorMsg = "无效的账号ID: " . (is_scalar($accountId) ? $accountId : gettype($accountId));
Log::error($errorMsg);
throw new Exception($errorMsg);
}
try {
Log::info("开始计算账号健康分accountId: {$accountId}, forceRecalculateBase: " . ($forceRecalculateBase ? 'true' : 'false'));
// 获取账号数据
if (empty($accountData)) {
$accountData = Db::table('s2_wechat_account')
$accountData = Db::table(self::TABLE_WECHAT_ACCOUNT)
->where('id', $accountId)
->find();
Log::debug("查询账号数据: " . ($accountData ? "成功" : "失败"));
}
if (empty($accountData)) {
throw new Exception("账号不存在:{$accountId}");
$errorMsg = "账号不存在:{$accountId}";
Log::error($errorMsg);
throw new Exception($errorMsg);
}
$wechatId = $accountData['wechatId'] ?? '';
if (empty($wechatId)) {
throw new Exception("账号wechatId为空{$accountId}");
$errorMsg = "账号wechatId为空{$accountId}";
Log::error($errorMsg);
throw new Exception($errorMsg);
}
Log::debug("账号数据: accountId={$accountId}, wechatId={$wechatId}");
// 获取或创建评分记录
$scoreRecord = $this->getOrCreateScoreRecord($accountId, $wechatId);
Log::debug("获取评分记录: " . ($scoreRecord ? "成功" : "失败"));
// 计算基础分(只计算一次,除非强制重新计算)
if (!$scoreRecord['baseScoreCalculated'] || $forceRecalculateBase) {
Log::info("计算基础分accountId: {$accountId}, baseScoreCalculated: " .
($scoreRecord['baseScoreCalculated'] ? 'true' : 'false') .
", forceRecalculateBase: " . ($forceRecalculateBase ? 'true' : 'false'));
$baseScoreData = $this->calculateBaseScore($accountData, $scoreRecord);
$this->updateBaseScore($accountId, $baseScoreData);
Log::debug("基础分计算结果: " . json_encode($baseScoreData));
// 重新获取记录以获取最新数据
$scoreRecord = $this->getScoreRecord($accountId);
}
// 计算动态分(每次都要重新计算)
Log::info("计算动态分accountId: {$accountId}");
$dynamicScoreData = $this->calculateDynamicScore($accountData, $scoreRecord);
// 计算总分
@@ -93,6 +152,9 @@ class WechatAccountHealthScoreService
// 计算每日最大加人次数
$maxAddFriendPerDay = $this->getMaxAddFriendPerDay($healthScore);
Log::info("健康分计算结果accountId: {$accountId}, baseScore: {$baseScore}, dynamicScore: {$dynamicScore}, " .
"healthScore: {$healthScore}, maxAddFriendPerDay: {$maxAddFriendPerDay}");
// 更新评分记录
$updateData = [
'dynamicScore' => $dynamicScore,
@@ -109,11 +171,16 @@ class WechatAccountHealthScoreService
'updateTime' => time()
];
Db::table('s2_wechat_account_score')
$updateResult = Db::table(self::TABLE_WECHAT_ACCOUNT_SCORE)
->where('accountId', $accountId)
->update($updateData);
// 更新成功后,清除缓存
if ($updateResult !== false) {
$this->clearScoreCache($accountId);
}
return [
$result = [
'accountId' => $accountId,
'wechatId' => $wechatId,
'healthScore' => $healthScore,
@@ -127,8 +194,19 @@ class WechatAccountHealthScoreService
'maxAddFriendPerDay' => $maxAddFriendPerDay
];
} catch (Exception $e) {
throw new Exception("计算健康分失败:" . $e->getMessage());
Log::debug("健康分计算完成,返回结果: " . json_encode($result));
return $result;
} catch (\PDOException $e) {
// 数据库异常
$errorMsg = "数据库操作失败accountId: {$accountId}, 错误: " . $e->getMessage();
Log::error($errorMsg);
throw new Exception($errorMsg, $e->getCode(), $e);
} catch (\Throwable $e) {
// 其他所有异常
$errorMsg = "计算健康分失败accountId: {$accountId}, 错误: " . $e->getMessage();
Log::error($errorMsg);
throw new Exception($errorMsg, $e->getCode(), $e);
}
}
@@ -141,11 +219,13 @@ class WechatAccountHealthScoreService
*/
private function getOrCreateScoreRecord($accountId, $wechatId)
{
$record = Db::table('s2_wechat_account_score')
->where('accountId', $accountId)
->find();
// 尝试获取现有记录
$record = $this->getScoreRecord($accountId);
// 如果记录不存在,创建新记录
if (empty($record)) {
Log::info("为账号 {$accountId} 创建新的评分记录");
// 创建新记录
$data = [
'accountId' => $accountId,
@@ -163,7 +243,7 @@ class WechatAccountHealthScoreService
'updateTime' => time()
];
Db::table('s2_wechat_account_score')->insert($data);
Db::table(self::TABLE_WECHAT_ACCOUNT_SCORE)->insert($data);
return $data;
}
@@ -175,13 +255,33 @@ class WechatAccountHealthScoreService
* 获取评分记录
*
* @param int $accountId 账号ID
* @return array
* @param bool $useCache 是否使用缓存默认true
* @return array 评分记录,如果不存在则返回空数组
*/
private function getScoreRecord($accountId)
private function getScoreRecord($accountId, $useCache = true)
{
return Db::table('s2_wechat_account_score')
// 生成缓存键
$cacheKey = self::CACHE_PREFIX . 'score:' . $accountId;
// 如果使用缓存且缓存存在,则直接返回缓存数据
if ($useCache && Cache::has($cacheKey)) {
$cachedData = Cache::get($cacheKey);
Log::debug("从缓存获取评分记录accountId: {$accountId}");
return $cachedData ?: [];
}
// 从数据库获取记录
$record = Db::table(self::TABLE_WECHAT_ACCOUNT_SCORE)
->where('accountId', $accountId)
->find() ?: [];
->find();
// 如果记录存在且使用缓存,则缓存记录
if ($record && $useCache) {
Cache::set($cacheKey, $record, self::CACHE_TTL);
Log::debug("缓存评分记录accountId: {$accountId}");
}
return $record ?: [];
}
/**
@@ -240,12 +340,41 @@ class WechatAccountHealthScoreService
*
* @param int $accountId 账号ID
* @param array $baseScoreData 基础分数据
* @return bool 更新是否成功
*/
private function updateBaseScore($accountId, $baseScoreData)
{
Db::table('s2_wechat_account_score')
->where('accountId', $accountId)
->update($baseScoreData);
try {
$result = Db::table(self::TABLE_WECHAT_ACCOUNT_SCORE)
->where('accountId', $accountId)
->update($baseScoreData);
Log::debug("更新基础分accountId: {$accountId}, 结果: " . ($result ? "成功" : "失败"));
// 更新成功后,清除缓存
if ($result !== false) {
$this->clearScoreCache($accountId);
}
return $result !== false;
} catch (Exception $e) {
Log::error("更新基础分失败accountId: {$accountId}, 错误: " . $e->getMessage());
return false;
}
}
/**
* 清除评分记录缓存
*
* @param int $accountId 账号ID
* @return bool 是否成功清除缓存
*/
private function clearScoreCache($accountId)
{
$cacheKey = self::CACHE_PREFIX . 'score:' . $accountId;
$result = Cache::rm($cacheKey);
Log::debug("清除评分记录缓存accountId: {$accountId}, 结果: " . ($result ? "成功" : "失败"));
return $result;
}
/**
@@ -310,16 +439,44 @@ class WechatAccountHealthScoreService
* @param int $accountId 账号ID
* @param int $friendCount 好友数量
* @param string $source 来源manual=手动sync=同步)
* @return bool
* @return bool 更新是否成功
* @throws Exception 如果参数无效或更新过程中出现错误
*/
public function updateFriendCountScore($accountId, $friendCount, $source = 'manual')
{
$scoreRecord = $this->getScoreRecord($accountId);
// 参数验证
if (empty($accountId) || !is_numeric($accountId)) {
$errorMsg = "无效的账号ID: " . (is_scalar($accountId) ? $accountId : gettype($accountId));
Log::error($errorMsg);
throw new Exception($errorMsg);
}
// 如果基础分已计算,不允许修改好友数量分(除非是手动更新)
if (!empty($scoreRecord['baseScoreCalculated']) && $source === 'sync') {
// 同步数据不允许修改已计算的基础分
return false;
if (!is_numeric($friendCount) || $friendCount < 0) {
$errorMsg = "无效的好友数量: {$friendCount}";
Log::error($errorMsg);
throw new Exception($errorMsg);
}
if (!in_array($source, ['manual', 'sync'])) {
$errorMsg = "无效的来源: {$source},必须是 'manual' 或 'sync'";
Log::error($errorMsg);
throw new Exception($errorMsg);
}
try {
$scoreRecord = $this->getScoreRecord($accountId);
// 如果基础分已计算,不允许修改好友数量分(除非是手动更新)
if (!empty($scoreRecord['baseScoreCalculated']) && $source === 'sync') {
// 同步数据不允许修改已计算的基础分
Log::warning("同步数据不允许修改已计算的基础分accountId: {$accountId}");
return false;
}
}
catch (\Exception $e) {
$errorMsg = "获取评分记录失败accountId: {$accountId}, 错误: " . $e->getMessage();
Log::error($errorMsg);
throw new Exception($errorMsg, $e->getCode(), $e);
}
$friendCountScore = $this->getFriendCountScore($friendCount);
@@ -348,16 +505,36 @@ class WechatAccountHealthScoreService
$updateData['maxAddFriendPerDay'] = $this->getMaxAddFriendPerDay($healthScore);
}
Db::table('s2_wechat_account_score')
->where('accountId', $accountId)
->update($updateData);
return true;
try {
$result = Db::table(self::TABLE_WECHAT_ACCOUNT_SCORE)
->where('accountId', $accountId)
->update($updateData);
// 更新成功后,清除缓存
if ($result !== false) {
$this->clearScoreCache($accountId);
$this->clearHealthScoreCache($accountId);
Log::info("更新好友数量分成功accountId: {$accountId}, friendCount: {$friendCount}, source: {$source}");
} else {
Log::warning("更新好友数量分失败accountId: {$accountId}, friendCount: {$friendCount}, source: {$source}");
}
return $result !== false;
} catch (\PDOException $e) {
$errorMsg = "数据库操作失败accountId: {$accountId}, 错误: " . $e->getMessage();
Log::error($errorMsg);
throw new Exception($errorMsg, $e->getCode(), $e);
} catch (\Throwable $e) {
$errorMsg = "更新好友数量分失败accountId: {$accountId}, 错误: " . $e->getMessage();
Log::error($errorMsg);
throw new Exception($errorMsg, $e->getCode(), $e);
}
}
/**
* 计算动态分
* 动态分 = 扣分 + 加分
* 如果添加好友记录表没有记录则动态分为0
*
* @param array $accountData 账号数据
* @param array $scoreRecord 现有评分记录
@@ -365,6 +542,11 @@ class WechatAccountHealthScoreService
*/
private function calculateDynamicScore($accountData, $scoreRecord)
{
$accountId = $accountData['id'] ?? 0;
$wechatId = $accountData['wechatId'] ?? '';
Log::debug("开始计算动态分accountId: {$accountId}, wechatId: {$wechatId}");
$result = [
'total' => 0,
'frequentPenalty' => 0,
@@ -377,13 +559,33 @@ class WechatAccountHealthScoreService
'isBanned' => 0
];
$accountId = $accountData['id'] ?? 0;
$wechatId = $accountData['wechatId'] ?? '';
if (empty($accountId) || empty($wechatId)) {
Log::warning("计算动态分失败: accountId或wechatId为空");
return $result;
}
// 计算30天前的时间戳在多个方法中使用
$thirtyDaysAgo = time() - (30 * 24 * 3600);
// 检查添加好友记录表是否有记录如果没有记录则动态分为0
// 使用EXISTS子查询优化性能只检查是否存在记录不需要计数
$hasFriendTask = Db::table(self::TABLE_FRIEND_TASK)
->where('wechatAccountId', $accountId)
->where(function($query) use ($wechatId) {
if (!empty($wechatId)) {
$query->where('wechatId', $wechatId);
}
})
->value('id'); // 只获取ID比count()更高效
// 如果添加好友记录表没有记录则动态分为0
if (empty($hasFriendTask)) {
Log::info("账号没有添加好友记录动态分为0accountId: {$accountId}");
return $result;
}
Log::debug("账号有添加好友记录继续计算动态分accountId: {$accountId}");
// 继承现有数据
if (!empty($scoreRecord)) {
$result['lastFrequentTime'] = $scoreRecord['lastFrequentTime'] ?? null;
@@ -396,20 +598,20 @@ class WechatAccountHealthScoreService
}
// 1. 检查频繁记录从s2_friend_task表查询只统计近30天
$frequentData = $this->checkFrequentFromFriendTask($accountId, $wechatId, $scoreRecord);
$frequentData = $this->checkFrequentFromFriendTask($accountId, $wechatId, $scoreRecord, $thirtyDaysAgo);
$result['lastFrequentTime'] = $frequentData['lastFrequentTime'] ?? null;
$result['frequentCount'] = $frequentData['frequentCount'] ?? 0;
$result['frequentPenalty'] = $frequentData['frequentPenalty'] ?? 0;
// 2. 检查封号记录从s2_wechat_message表查询
$banData = $this->checkBannedFromMessage($accountId, $wechatId);
$banData = $this->checkBannedFromMessage($accountId, $wechatId, $thirtyDaysAgo);
if (!empty($banData)) {
$result['isBanned'] = $banData['isBanned'];
$result['banPenalty'] = $banData['banPenalty'];
}
// 3. 计算不频繁加分基于近30天的频繁记录反向参考频繁规则
$noFrequentData = $this->calculateNoFrequentBonus($accountId, $wechatId, $frequentData);
$noFrequentData = $this->calculateNoFrequentBonus($accountId, $wechatId, $frequentData, $thirtyDaysAgo);
$result['noFrequentBonus'] = $noFrequentData['bonus'] ?? 0;
$result['consecutiveNoFrequentDays'] = $noFrequentData['consecutiveDays'] ?? 0;
$result['lastNoFrequentTime'] = $noFrequentData['lastNoFrequentTime'] ?? null;
@@ -417,6 +619,10 @@ class WechatAccountHealthScoreService
// 计算总分
$result['total'] = $result['frequentPenalty'] + $result['noFrequentBonus'] + $result['banPenalty'];
Log::debug("动态分计算结果accountId: {$accountId}, frequentPenalty: {$result['frequentPenalty']}, " .
"noFrequentBonus: {$result['noFrequentBonus']}, banPenalty: {$result['banPenalty']}, " .
"total: {$result['total']}");
return $result;
}
@@ -428,16 +634,20 @@ class WechatAccountHealthScoreService
* @param int $accountId 账号ID
* @param string $wechatId 微信ID
* @param array $scoreRecord 现有评分记录
* @param int $thirtyDaysAgo 30天前的时间戳可选如果已计算则传入以避免重复计算
* @return array|null
*/
private function checkFrequentFromFriendTask($accountId, $wechatId, $scoreRecord)
private function checkFrequentFromFriendTask($accountId, $wechatId, $scoreRecord, $thirtyDaysAgo = null)
{
// 计算30天前的时间戳
$thirtyDaysAgo = time() - (30 * 24 * 3600);
// 如果没有传入30天前的时间戳,则计算
if ($thirtyDaysAgo === null) {
$thirtyDaysAgo = time() - (30 * 24 * 3600);
}
// 查询包含"操作过于频繁"的记录只统计近30天
// extra字段可能是文本或JSON格式使用LIKE查询
$frequentTasks = Db::table('s2_friend_task')
// 优化查询:只查询必要的字段,减少数据传输量
$frequentTasks = Db::table(self::TABLE_FRIEND_TASK)
->where('wechatAccountId', $accountId)
->where('createTime', '>=', $thirtyDaysAgo)
->where(function($query) use ($wechatId) {
@@ -448,7 +658,7 @@ class WechatAccountHealthScoreService
->where(function($query) {
// 检查extra字段是否包含"操作过于频繁"可能是文本或JSON
$query->where('extra', 'like', '%操作过于频繁%')
->whereOr('extra', 'like', '%"操作过于频繁"%');
->whereOr('extra', 'like', '%"当前账号存在安全风险"%');
})
->order('createTime', 'desc')
->field('id, createTime, extra')
@@ -491,20 +701,25 @@ class WechatAccountHealthScoreService
*
* @param int $accountId 账号ID
* @param string $wechatId 微信ID
* @param int $thirtyDaysAgo 30天前的时间戳可选如果已计算则传入以避免重复计算
* @return array|null
*/
private function checkBannedFromMessage($accountId, $wechatId)
private function checkBannedFromMessage($accountId, $wechatId, $thirtyDaysAgo = null)
{
// 计算30天前的时间戳
$thirtyDaysAgo = time() - (30 * 24 * 3600);
// 如果没有传入30天前的时间戳,则计算
if ($thirtyDaysAgo === null) {
$thirtyDaysAgo = time() - (30 * 24 * 3600);
}
// 查询封号消息只统计近30天
$banMessage = Db::table('s2_wechat_message')
// 优化查询:只查询必要的字段,减少数据传输量
$banMessage = Db::table(self::TABLE_WECHAT_MESSAGE)
->where('wechatAccountId', $accountId)
->where('msgType', 10000)
->where('content', 'like', '%你的账号被限制%')
->where('isDeleted', 0)
->where('createTime', '>=', $thirtyDaysAgo)
->field('id, createTime') // 只查询必要的字段
->order('createTime', 'desc')
->find();
@@ -530,9 +745,10 @@ class WechatAccountHealthScoreService
* @param int $accountId 账号ID
* @param string $wechatId 微信ID
* @param array $frequentData 频繁数据包含lastFrequentTime和frequentCount
* @param int $thirtyDaysAgo 30天前的时间戳可选如果已计算则传入以避免重复计算
* @return array 包含bonus、consecutiveDays、lastNoFrequentTime
*/
private function calculateNoFrequentBonus($accountId, $wechatId, $frequentData)
private function calculateNoFrequentBonus($accountId, $wechatId, $frequentData, $thirtyDaysAgo = null)
{
$result = [
'bonus' => 0,
@@ -544,8 +760,10 @@ class WechatAccountHealthScoreService
return $result;
}
// 计算30天前的时间戳
$thirtyDaysAgo = time() - (30 * 24 * 3600);
// 如果没有传入30天前的时间戳,则计算
if ($thirtyDaysAgo === null) {
$thirtyDaysAgo = time() - (30 * 24 * 3600);
}
$currentTime = time();
// 获取最后一次频繁时间30天内最后一次频繁的时间
@@ -601,29 +819,54 @@ class WechatAccountHealthScoreService
* @param int $batchSize 每批处理数量
* @param bool $forceRecalculateBase 是否强制重新计算基础分
* @return array 处理结果统计
* @throws Exception 如果参数无效或批量处理过程中出现严重错误
*/
public function batchCalculateAndUpdate($accountIds = [], $batchSize = 100, $forceRecalculateBase = false)
{
$stats = [
'total' => 0,
'success' => 0,
'failed' => 0,
'errors' => []
];
// 如果没有指定账号ID则处理所有账号
if (empty($accountIds)) {
$accountIds = Db::table('s2_wechat_account')
->where('isDeleted', 0)
->column('id');
// 参数验证
if (!is_array($accountIds)) {
$errorMsg = "无效的账号ID数组: " . gettype($accountIds);
Log::error($errorMsg);
throw new Exception($errorMsg);
}
if (!is_numeric($batchSize) || $batchSize <= 0) {
$errorMsg = "无效的批处理大小: {$batchSize}";
Log::error($errorMsg);
throw new Exception($errorMsg);
}
try {
$startTime = microtime(true);
Log::info("开始批量计算健康分batchSize: {$batchSize}, forceRecalculateBase: " . ($forceRecalculateBase ? 'true' : 'false'));
$stats = [
'total' => 0,
'success' => 0,
'failed' => 0,
'errors' => []
];
// 如果没有指定账号ID则处理所有账号
if (empty($accountIds)) {
Log::info("未指定账号ID处理所有未删除账号");
$accountIds = Db::table(self::TABLE_WECHAT_ACCOUNT)
->where('isDeleted', 0)
->column('id');
}
$stats['total'] = count($accountIds);
Log::info("需要处理的账号总数: {$stats['total']}");
// 分批处理
$batches = array_chunk($accountIds, $batchSize);
$batchCount = count($batches);
Log::info("分批处理,共 {$batchCount}");
foreach ($batches as $batch) {
foreach ($batches as $batchIndex => $batch) {
$batchStartTime = microtime(true);
Log::info("开始处理第 " . ($batchIndex + 1) . " 批,共 " . count($batch) . " 个账号");
foreach ($batch as $accountId) {
try {
$this->calculateAndUpdate($accountId, null, $forceRecalculateBase);
@@ -634,11 +877,30 @@ class WechatAccountHealthScoreService
'accountId' => $accountId,
'error' => $e->getMessage()
];
Log::error("账号 {$accountId} 计算失败: " . $e->getMessage());
}
}
$batchEndTime = microtime(true);
$batchDuration = round($batchEndTime - $batchStartTime, 2);
Log::info("" . ($batchIndex + 1) . " 批处理完成,耗时: {$batchDuration}秒," .
"成功: {$stats['success']},失败: {$stats['failed']}");
}
$endTime = microtime(true);
$totalDuration = round($endTime - $startTime, 2);
Log::info("批量计算健康分完成,总耗时: {$totalDuration}秒,成功: {$stats['success']},失败: {$stats['failed']}");
return $stats;
} catch (\PDOException $e) {
$errorMsg = "批量计算健康分过程中数据库操作失败: " . $e->getMessage();
Log::error($errorMsg);
throw new Exception($errorMsg, $e->getCode(), $e);
} catch (\Throwable $e) {
$errorMsg = "批量计算健康分过程中发生严重错误: " . $e->getMessage();
Log::error($errorMsg);
throw new Exception($errorMsg, $e->getCode(), $e);
}
}
/**
@@ -672,7 +934,7 @@ class WechatAccountHealthScoreService
if (empty($scoreRecord)) {
// 如果记录不存在,先创建
$accountData = Db::table('s2_wechat_account')
$accountData = Db::table(self::TABLE_WECHAT_ACCOUNT)
->where('id', $accountId)
->find();
@@ -725,17 +987,36 @@ class WechatAccountHealthScoreService
* 获取账号健康分信息
*
* @param int $accountId 账号ID
* @param bool $useCache 是否使用缓存默认true
* @param bool $forceRecalculate 是否强制重新计算默认false
* @return array|null
*/
public function getHealthScore($accountId)
public function getHealthScore($accountId, $useCache = true, $forceRecalculate = false)
{
$scoreRecord = $this->getScoreRecord($accountId);
// 如果强制重新计算,则不使用缓存
if ($forceRecalculate) {
Log::info("强制重新计算健康分accountId: {$accountId}");
return $this->calculateAndUpdate($accountId, null, false);
}
// 生成缓存键
$cacheKey = self::CACHE_PREFIX . 'health:' . $accountId;
// 如果使用缓存且缓存存在,则直接返回缓存数据
if ($useCache && !$forceRecalculate && Cache::has($cacheKey)) {
$cachedData = Cache::get($cacheKey);
Log::debug("从缓存获取健康分信息accountId: {$accountId}");
return $cachedData;
}
// 从数据库获取记录
$scoreRecord = $this->getScoreRecord($accountId, $useCache);
if (empty($scoreRecord)) {
return null;
}
return [
$healthScoreInfo = [
'accountId' => $scoreRecord['accountId'],
'wechatId' => $scoreRecord['wechatId'],
'healthScore' => $scoreRecord['healthScore'] ?? 0,
@@ -753,5 +1034,31 @@ class WechatAccountHealthScoreService
'frequentCount' => $scoreRecord['frequentCount'] ?? 0,
'isBanned' => $scoreRecord['isBanned'] ?? 0
];
// 如果使用缓存,则缓存健康分信息
if ($useCache) {
Cache::set($cacheKey, $healthScoreInfo, self::CACHE_TTL);
Log::debug("缓存健康分信息accountId: {$accountId}");
}
return $healthScoreInfo;
}
/**
* 清除健康分信息缓存
*
* @param int $accountId 账号ID
* @return bool 是否成功清除缓存
*/
public function clearHealthScoreCache($accountId)
{
$cacheKey = self::CACHE_PREFIX . 'health:' . $accountId;
$result = Cache::rm($cacheKey);
// 同时清除评分记录缓存
$this->clearScoreCache($accountId);
Log::debug("清除健康分信息缓存accountId: {$accountId}, 结果: " . ($result ? "成功" : "失败"));
return $result;
}
}