312 lines
12 KiB
PHP
312 lines
12 KiB
PHP
<?php
|
||
|
||
namespace app\command;
|
||
|
||
use think\console\Command;
|
||
use think\console\Input;
|
||
use think\console\Output;
|
||
use think\Db;
|
||
use app\common\service\WechatAccountHealthScoreService;
|
||
|
||
/**
|
||
* 统一计算微信账号健康分命令
|
||
* 一个命令完成所有评分工作:
|
||
* 1. 初始化未计算的账号(基础分只计算一次)
|
||
* 2. 更新评分记录(根据wechatId和alias不一致情况)
|
||
* 3. 批量更新健康分(只更新动态分)
|
||
*/
|
||
class CalculateWechatAccountScoreCommand extends Command
|
||
{
|
||
protected function configure()
|
||
{
|
||
$this->setName('wechat:calculate-score')
|
||
->setDescription('统一计算微信账号健康分(包含初始化、更新评分记录、批量计算)');
|
||
}
|
||
|
||
protected function execute(Input $input, Output $output)
|
||
{
|
||
$output->writeln("==========================================");
|
||
$output->writeln("开始统一计算微信账号健康分...");
|
||
$output->writeln("==========================================");
|
||
|
||
$startTime = time();
|
||
$service = new WechatAccountHealthScoreService();
|
||
|
||
try {
|
||
// 步骤1: 初始化未计算基础分的账号
|
||
$output->writeln("\n[步骤1] 初始化未计算基础分的账号...");
|
||
$initStats = $this->initUncalculatedAccounts($service, $output);
|
||
$output->writeln("初始化完成:成功 {$initStats['success']} 条,失败 {$initStats['failed']} 条");
|
||
|
||
// 步骤2: 更新评分记录(根据wechatId和alias不一致情况)
|
||
$output->writeln("\n[步骤2] 更新评分记录(根据wechatId和alias不一致情况)...");
|
||
$updateStats = $this->updateScoreRecords($service, $output);
|
||
$output->writeln("更新完成:处理了 {$updateStats['total']} 条记录");
|
||
|
||
// 步骤3: 批量更新健康分(只更新动态分,不重新计算基础分)
|
||
$output->writeln("\n[步骤3] 批量更新健康分(只更新动态分)...");
|
||
$batchStats = $this->batchUpdateHealthScore($service, $output);
|
||
$output->writeln("批量更新完成:成功 {$batchStats['success']} 条,失败 {$batchStats['failed']} 条");
|
||
|
||
// 统计信息
|
||
$endTime = time();
|
||
$duration = $endTime - $startTime;
|
||
|
||
$output->writeln("\n==========================================");
|
||
$output->writeln("任务完成!");
|
||
$output->writeln("==========================================");
|
||
$output->writeln("总耗时: {$duration} 秒");
|
||
$output->writeln("初始化: 成功 {$initStats['success']} 条,失败 {$initStats['failed']} 条");
|
||
$output->writeln("更新评分记录: {$updateStats['total']} 条");
|
||
$output->writeln("批量更新: 成功 {$batchStats['success']} 条,失败 {$batchStats['failed']} 条");
|
||
|
||
if (!empty($initStats['errors'])) {
|
||
$output->writeln("\n初始化错误详情:");
|
||
foreach (array_slice($initStats['errors'], 0, 10) as $error) {
|
||
$output->writeln(" 账号ID {$error['accountId']}: {$error['error']}");
|
||
}
|
||
if (count($initStats['errors']) > 10) {
|
||
$output->writeln(" ... 还有 " . (count($initStats['errors']) - 10) . " 个错误");
|
||
}
|
||
}
|
||
|
||
if (!empty($batchStats['errors'])) {
|
||
$output->writeln("\n批量更新错误详情:");
|
||
foreach (array_slice($batchStats['errors'], 0, 10) as $error) {
|
||
$output->writeln(" 账号ID {$error['accountId']}: {$error['error']}");
|
||
}
|
||
if (count($batchStats['errors']) > 10) {
|
||
$output->writeln(" ... 还有 " . (count($batchStats['errors']) - 10) . " 个错误");
|
||
}
|
||
}
|
||
|
||
} catch (\Exception $e) {
|
||
$output->writeln("\n错误: " . $e->getMessage());
|
||
$output->writeln($e->getTraceAsString());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化未计算基础分的账号
|
||
*
|
||
* @param WechatAccountHealthScoreService $service
|
||
* @param Output $output
|
||
* @return array
|
||
*/
|
||
private function initUncalculatedAccounts($service, $output)
|
||
{
|
||
$stats = [
|
||
'total' => 0,
|
||
'success' => 0,
|
||
'failed' => 0,
|
||
'errors' => []
|
||
];
|
||
|
||
// 获取所有未计算基础分的账号
|
||
$accounts = Db::table('s2_wechat_account')
|
||
->alias('a')
|
||
->leftJoin(['s2_wechat_account_score' => 's'], 's.accountId = a.id')
|
||
->where('a.isDeleted', 0)
|
||
->where(function($query) {
|
||
$query->whereNull('s.id')
|
||
->whereOr('s.baseScoreCalculated', 0);
|
||
})
|
||
->field('a.id, a.wechatId')
|
||
->select();
|
||
|
||
$stats['total'] = count($accounts);
|
||
|
||
if ($stats['total'] == 0) {
|
||
$output->writeln("没有需要初始化的账号");
|
||
return $stats;
|
||
}
|
||
|
||
$output->writeln("找到 {$stats['total']} 个需要初始化的账号");
|
||
|
||
$batchSize = 100;
|
||
$batches = array_chunk($accounts, $batchSize);
|
||
|
||
foreach ($batches as $batchIndex => $batch) {
|
||
foreach ($batch as $account) {
|
||
try {
|
||
$service->calculateAndUpdate($account['id']);
|
||
$stats['success']++;
|
||
|
||
if ($stats['success'] % 100 == 0) {
|
||
$output->write(".");
|
||
}
|
||
} catch (\Exception $e) {
|
||
$stats['failed']++;
|
||
$stats['errors'][] = [
|
||
'accountId' => $account['id'],
|
||
'error' => $e->getMessage()
|
||
];
|
||
}
|
||
}
|
||
|
||
if (($batchIndex + 1) % 10 == 0) {
|
||
$output->writeln(" 已处理 " . ($batchIndex + 1) * $batchSize . " 条");
|
||
}
|
||
}
|
||
|
||
return $stats;
|
||
}
|
||
|
||
/**
|
||
* 更新评分记录(根据wechatId和alias不一致情况)
|
||
*
|
||
* @param WechatAccountHealthScoreService $service
|
||
* @param Output $output
|
||
* @return array
|
||
*/
|
||
private function updateScoreRecords($service, $output)
|
||
{
|
||
$stats = ['total' => 0];
|
||
|
||
// 查找wechatId和alias不一致的账号
|
||
$inconsistentAccounts = Db::table('s2_wechat_account')
|
||
->where('isDeleted', 0)
|
||
->where('wechatId', '<>', '')
|
||
->where('alias', '<>', '')
|
||
->whereRaw('wechatId != alias')
|
||
->field('id, wechatId, alias')
|
||
->select();
|
||
|
||
// 查找wechatId和alias一致的账号
|
||
$consistentAccounts = Db::table('s2_wechat_account')
|
||
->where('isDeleted', 0)
|
||
->where('wechatId', '<>', '')
|
||
->where('alias', '<>', '')
|
||
->whereRaw('wechatId = alias')
|
||
->field('id, wechatId, alias')
|
||
->select();
|
||
|
||
$allAccounts = array_merge($inconsistentAccounts, $consistentAccounts);
|
||
$stats['total'] = count($allAccounts);
|
||
|
||
if ($stats['total'] == 0) {
|
||
$output->writeln("没有需要更新的账号");
|
||
return $stats;
|
||
}
|
||
|
||
$output->writeln("找到 {$stats['total']} 个需要更新的账号(不一致: " . count($inconsistentAccounts) . ",一致: " . count($consistentAccounts) . ")");
|
||
|
||
$updatedCount = 0;
|
||
|
||
foreach ($allAccounts as $account) {
|
||
$isModifiedAlias = in_array($account['id'], array_column($inconsistentAccounts, 'id'));
|
||
$this->updateScoreRecord($account['id'], $isModifiedAlias, $service);
|
||
$updatedCount++;
|
||
|
||
if ($updatedCount % 100 == 0) {
|
||
$output->write(".");
|
||
}
|
||
}
|
||
|
||
if ($updatedCount > 0 && $updatedCount % 100 == 0) {
|
||
$output->writeln("");
|
||
}
|
||
|
||
return $stats;
|
||
}
|
||
|
||
/**
|
||
* 批量更新健康分(只更新动态分)
|
||
*
|
||
* @param WechatAccountHealthScoreService $service
|
||
* @param Output $output
|
||
* @return array
|
||
*/
|
||
private function batchUpdateHealthScore($service, $output)
|
||
{
|
||
// 获取所有已计算基础分的账号
|
||
$accountIds = Db::table('s2_wechat_account_score')
|
||
->where('baseScoreCalculated', 1)
|
||
->column('accountId');
|
||
|
||
$total = count($accountIds);
|
||
|
||
if ($total == 0) {
|
||
$output->writeln("没有需要更新的账号");
|
||
return ['success' => 0, 'failed' => 0, 'errors' => []];
|
||
}
|
||
|
||
$output->writeln("找到 {$total} 个需要更新动态分的账号");
|
||
|
||
$stats = $service->batchCalculateAndUpdate($accountIds, 100, false);
|
||
|
||
return $stats;
|
||
}
|
||
|
||
/**
|
||
* 更新评分记录
|
||
*
|
||
* @param int $accountId 账号ID
|
||
* @param bool $isModifiedAlias 是否已修改微信号
|
||
* @param WechatAccountHealthScoreService $service 评分服务
|
||
*/
|
||
private function updateScoreRecord($accountId, $isModifiedAlias, $service)
|
||
{
|
||
// 获取账号数据
|
||
$accountData = Db::table('s2_wechat_account')
|
||
->where('id', $accountId)
|
||
->find();
|
||
|
||
if (empty($accountData)) {
|
||
return;
|
||
}
|
||
|
||
// 确保评分记录存在
|
||
$scoreRecord = Db::table('s2_wechat_account_score')
|
||
->where('accountId', $accountId)
|
||
->find();
|
||
|
||
if (empty($scoreRecord)) {
|
||
// 如果记录不存在,创建并计算基础分
|
||
$service->calculateAndUpdate($accountId);
|
||
$scoreRecord = Db::table('s2_wechat_account_score')
|
||
->where('accountId', $accountId)
|
||
->find();
|
||
}
|
||
|
||
if (empty($scoreRecord)) {
|
||
return;
|
||
}
|
||
|
||
// 更新isModifiedAlias字段
|
||
$updateData = [
|
||
'isModifiedAlias' => $isModifiedAlias ? 1 : 0,
|
||
'updateTime' => time()
|
||
];
|
||
|
||
// 如果基础分已计算,需要更新基础信息分和基础分
|
||
if ($scoreRecord['baseScoreCalculated']) {
|
||
$oldBaseInfoScore = $scoreRecord['baseInfoScore'] ?? 0;
|
||
$newBaseInfoScore = $isModifiedAlias ? 10 : 0; // 已修改微信号得10分
|
||
|
||
if ($oldBaseInfoScore != $newBaseInfoScore) {
|
||
$oldBaseScore = $scoreRecord['baseScore'] ?? 60;
|
||
$newBaseScore = $oldBaseScore - $oldBaseInfoScore + $newBaseInfoScore;
|
||
|
||
$updateData['baseInfoScore'] = $newBaseInfoScore;
|
||
$updateData['baseScore'] = $newBaseScore;
|
||
|
||
// 重新计算健康分
|
||
$dynamicScore = $scoreRecord['dynamicScore'] ?? 0;
|
||
$healthScore = $newBaseScore + $dynamicScore;
|
||
$healthScore = max(0, min(100, $healthScore));
|
||
$updateData['healthScore'] = $healthScore;
|
||
$updateData['maxAddFriendPerDay'] = (int)floor($healthScore * 0.2);
|
||
}
|
||
} else {
|
||
// 基础分未计算,只更新标记和基础信息分
|
||
$updateData['baseInfoScore'] = $isModifiedAlias ? 10 : 0;
|
||
}
|
||
|
||
Db::table('s2_wechat_account_score')
|
||
->where('accountId', $accountId)
|
||
->update($updateData);
|
||
}
|
||
}
|
||
|