优化每日推送创建(更新)用户资产
This commit is contained in:
160
app/common/library/MallDailyPushBackfill.php
Normal file
160
app/common/library/MallDailyPushBackfill.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
use app\common\model\MallDailyPush;
|
||||
use app\common\model\MallUserAsset;
|
||||
use ba\Random;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 基于历史每日推送记录回补/同步用户资产主信息。
|
||||
*/
|
||||
class MallDailyPushBackfill
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function backfill(?string $dateFrom, ?string $dateTo, int $limit = 0, bool $dryRun = false): array
|
||||
{
|
||||
$query = MallDailyPush::order('create_time', 'desc')->order('id', 'desc');
|
||||
if ($dateFrom !== null && $dateFrom !== '') {
|
||||
$query->where('date', '>=', $dateFrom);
|
||||
}
|
||||
if ($dateTo !== null && $dateTo !== '') {
|
||||
$query->where('date', '<=', $dateTo);
|
||||
}
|
||||
|
||||
$rows = $query->select();
|
||||
$seenUserIds = [];
|
||||
|
||||
$stats = [
|
||||
'dry_run' => $dryRun,
|
||||
'date_from' => $dateFrom,
|
||||
'date_to' => $dateTo,
|
||||
'limit' => $limit,
|
||||
'scanned_rows' => 0,
|
||||
'target_users' => 0,
|
||||
'processed_users' => 0,
|
||||
'created_users' => 0,
|
||||
'updated_users' => 0,
|
||||
'unchanged_users' => 0,
|
||||
'failed_users' => 0,
|
||||
'errors' => [],
|
||||
];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$stats['scanned_rows']++;
|
||||
$playxUserId = trim(strval($row->user_id ?? ''));
|
||||
if ($playxUserId === '' || isset($seenUserIds[$playxUserId])) {
|
||||
continue;
|
||||
}
|
||||
$seenUserIds[$playxUserId] = true;
|
||||
$stats['target_users']++;
|
||||
|
||||
if ($limit > 0 && $stats['target_users'] > $limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
$username = trim(strval($row->username ?? ''));
|
||||
try {
|
||||
$result = $this->ensureAssetForPlayx($playxUserId, $username, $dryRun);
|
||||
$stats['processed_users']++;
|
||||
if ($result['created']) {
|
||||
$stats['created_users']++;
|
||||
} elseif ($result['updated']) {
|
||||
$stats['updated_users']++;
|
||||
} else {
|
||||
$stats['unchanged_users']++;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$stats['failed_users']++;
|
||||
$stats['errors'][] = [
|
||||
'user_id' => $playxUserId,
|
||||
'message' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{created:bool,updated:bool}
|
||||
*/
|
||||
private function ensureAssetForPlayx(string $playxUserId, string $username, bool $dryRun): array
|
||||
{
|
||||
$asset = MallUserAsset::where('playx_user_id', $playxUserId)->find();
|
||||
if ($asset) {
|
||||
$updated = false;
|
||||
$uname = trim($username);
|
||||
if ($uname !== '' && strval($asset->username ?? '') !== $uname) {
|
||||
$asset->username = $uname;
|
||||
$updated = true;
|
||||
}
|
||||
if ($updated && !$dryRun) {
|
||||
$asset->save();
|
||||
}
|
||||
return ['created' => false, 'updated' => $updated];
|
||||
}
|
||||
|
||||
$effectiveUsername = trim($username);
|
||||
if ($effectiveUsername === '') {
|
||||
$effectiveUsername = 'playx_' . $playxUserId;
|
||||
}
|
||||
|
||||
$byName = MallUserAsset::where('username', $effectiveUsername)->find();
|
||||
if ($byName) {
|
||||
$updated = strval($byName->playx_user_id ?? '') !== $playxUserId;
|
||||
if ($updated && !$dryRun) {
|
||||
$byName->playx_user_id = $playxUserId;
|
||||
$byName->save();
|
||||
}
|
||||
return ['created' => false, 'updated' => $updated];
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
return ['created' => true, 'updated' => false];
|
||||
}
|
||||
|
||||
$phone = $this->buildTempPhone();
|
||||
if ($phone === null) {
|
||||
throw new \RuntimeException('Failed to allocate phone for playx user');
|
||||
}
|
||||
$pwd = hash_password(Random::build('alnum', 16));
|
||||
$now = time();
|
||||
$created = MallUserAsset::create([
|
||||
'playx_user_id' => $playxUserId,
|
||||
'username' => $effectiveUsername,
|
||||
'phone' => $phone,
|
||||
'password' => $pwd,
|
||||
'admin_id' => 0,
|
||||
'locked_points' => 0,
|
||||
'available_points' => 0,
|
||||
'today_limit' => 0,
|
||||
'today_claimed' => 0,
|
||||
'today_limit_date' => null,
|
||||
'create_time' => $now,
|
||||
'update_time' => $now,
|
||||
]);
|
||||
|
||||
if (!$created) {
|
||||
throw new \RuntimeException('Failed to create mall_user_asset');
|
||||
}
|
||||
return ['created' => true, 'updated' => false];
|
||||
}
|
||||
|
||||
private function buildTempPhone(): ?string
|
||||
{
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$candidate = '13' . str_pad(strval(mt_rand(0, 999999999)), 9, '0', STR_PAD_LEFT);
|
||||
if (!MallUserAsset::where('phone', $candidate)->find()) {
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user