72 lines
2.5 KiB
PHP
72 lines
2.5 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\common\service;
|
||
|
||
/** 一次 HTTP 请求只拉一页;只有归档成功后才推进游标。 */
|
||
final class ImChatSyncSession
|
||
{
|
||
public static function start(array $accounts): array
|
||
{
|
||
return [
|
||
'accounts' => array_values(array_unique($accounts)),
|
||
'index' => 0, 'side' => 0, 'cursor' => [], 'inserted' => 0, 'errors' => [],
|
||
];
|
||
}
|
||
|
||
public static function step(array $state, callable $fetchPage, callable $archive): array
|
||
{
|
||
if ($state['index'] >= count($state['accounts'])) {
|
||
return $state;
|
||
}
|
||
$account = $state['accounts'][$state['index']];
|
||
try {
|
||
$page = $fetchPage($account, $state['cursor']);
|
||
} catch (\Throwable $e) {
|
||
// 失败的会话不妨碍其他医生记录落库,下一轮仍从头补拉该会话。
|
||
$state['errors'][] = $account . ($state['side'] === 0 ? '(医生侧)' : '(患者侧)') . ':' . $e->getMessage();
|
||
return self::nextSide($state);
|
||
}
|
||
|
||
// 落库异常交由调用者处理,绝不能把未归档的页标记为已同步。
|
||
$state['inserted'] += $archive($account, $page['msgList']);
|
||
if ($page['completed']) {
|
||
$state = self::nextSide($state);
|
||
} else {
|
||
$state['cursor'] = $page['cursor'];
|
||
}
|
||
return $state;
|
||
}
|
||
|
||
private static function nextSide(array $state): array
|
||
{
|
||
$state['cursor'] = [];
|
||
if ($state['side'] === 0) {
|
||
$state['side'] = 1;
|
||
} else {
|
||
$state['side'] = 0;
|
||
$state['index']++;
|
||
}
|
||
return $state;
|
||
}
|
||
|
||
public static function progress(array $state): array
|
||
{
|
||
$checking = array_key_exists('accounts_verified', $state) && !$state['accounts_verified'];
|
||
$check = $state['account_check'] ?? null;
|
||
$completed = !$checking && $state['index'] >= count($state['accounts']);
|
||
return [
|
||
'completed' => $completed,
|
||
'phase' => $checking ? 'checking_accounts' : ($completed ? 'completed' : 'syncing'),
|
||
'checked_accounts' => $check['offset'] ?? 0,
|
||
'candidate_accounts' => $check ? count($check['candidates']) : 0,
|
||
'skipped_accounts' => $check ? count($check['missing']) : 0,
|
||
'inserted' => $state['inserted'],
|
||
'processed_peers' => $state['index'],
|
||
'total_peers' => count($state['accounts']),
|
||
'errors' => $state['errors'],
|
||
];
|
||
}
|
||
}
|