|
@@ -6,19 +6,15 @@ use App\Models\BalanceLog;
|
|
|
use App\Services\ThirdGameBalanceService;
|
|
use App\Services\ThirdGameBalanceService;
|
|
|
use Carbon\Carbon;
|
|
use Carbon\Carbon;
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Console\Command;
|
|
|
-use Illuminate\Support\Facades\File;
|
|
|
|
|
|
|
|
|
|
class ThirdGameBalanceUsers extends Command
|
|
class ThirdGameBalanceUsers extends Command
|
|
|
{
|
|
{
|
|
|
protected $signature = 'third-game:balance-users
|
|
protected $signature = 'third-game:balance-users
|
|
|
{--from= : 开始时间,例如 2026-07-14 10:00:00}
|
|
{--from= : 开始时间,例如 2026-07-14 10:00:00}
|
|
|
{--to= : 结束时间,例如 2026-07-14 23:59:59}
|
|
{--to= : 结束时间,例如 2026-07-14 23:59:59}
|
|
|
- {--member_id=* : 只查询指定 member_id,可重复传入}
|
|
|
|
|
- {--csv= : CSV 输出路径,默认保存到 storage/app}
|
|
|
|
|
- {--show=100 : 控制台最多展示条数}
|
|
|
|
|
- {--skip-provider : 仅导出本地流水,不请求三方当前余额}';
|
|
|
|
|
|
|
+ {--member_id=* : 只查询指定 member_id,可重复传入}';
|
|
|
|
|
|
|
|
- protected $description = '导出有三方游戏流水的玩家,并查询三方当前余额(只读,不回收余额)';
|
|
|
|
|
|
|
+ protected $description = '显示有三方游戏流水的玩家及当前三方总余额(只读,不回收余额)';
|
|
|
|
|
|
|
|
public function handle(ThirdGameBalanceService $service): int
|
|
public function handle(ThirdGameBalanceService $service): int
|
|
|
{
|
|
{
|
|
@@ -35,38 +31,11 @@ class ThirdGameBalanceUsers extends Command
|
|
|
return self::FAILURE;
|
|
return self::FAILURE;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $transferInType = '三方游戏转入';
|
|
|
|
|
- $transferOutType = '三方游戏转出';
|
|
|
|
|
$query = BalanceLog::query()
|
|
$query = BalanceLog::query()
|
|
|
->whereNotNull('member_id')
|
|
->whereNotNull('member_id')
|
|
|
->where('member_id', '<>', '')
|
|
->where('member_id', '<>', '')
|
|
|
- ->whereIn('change_type', [$transferInType, $transferOutType])
|
|
|
|
|
- ->select('member_id')
|
|
|
|
|
- ->selectRaw(
|
|
|
|
|
- 'SUM(CASE WHEN change_type = ? AND amount < 0 THEN 1 ELSE 0 END) AS transfer_in_count',
|
|
|
|
|
- [$transferInType]
|
|
|
|
|
- )
|
|
|
|
|
- ->selectRaw(
|
|
|
|
|
- 'SUM(CASE WHEN change_type = ? AND amount < 0 THEN -amount ELSE 0 END) AS transfer_in_total',
|
|
|
|
|
- [$transferInType]
|
|
|
|
|
- )
|
|
|
|
|
- ->selectRaw(
|
|
|
|
|
- 'SUM(CASE WHEN change_type = ? AND amount > 0 THEN 1 ELSE 0 END) AS refund_count',
|
|
|
|
|
- [$transferInType]
|
|
|
|
|
- )
|
|
|
|
|
- ->selectRaw(
|
|
|
|
|
- 'SUM(CASE WHEN change_type = ? AND amount > 0 THEN amount ELSE 0 END) AS refund_total',
|
|
|
|
|
- [$transferInType]
|
|
|
|
|
- )
|
|
|
|
|
- ->selectRaw(
|
|
|
|
|
- 'SUM(CASE WHEN change_type = ? AND amount > 0 THEN 1 ELSE 0 END) AS transfer_out_count',
|
|
|
|
|
- [$transferOutType]
|
|
|
|
|
- )
|
|
|
|
|
- ->selectRaw(
|
|
|
|
|
- 'SUM(CASE WHEN change_type = ? AND amount > 0 THEN amount ELSE 0 END) AS transfer_out_total',
|
|
|
|
|
- [$transferOutType]
|
|
|
|
|
- )
|
|
|
|
|
- ->selectRaw('MIN(created_at) AS first_at, MAX(created_at) AS last_at');
|
|
|
|
|
|
|
+ ->whereIn('change_type', ['三方游戏转入', '三方游戏转出'])
|
|
|
|
|
+ ->select('member_id');
|
|
|
|
|
|
|
|
if ($from !== null) {
|
|
if ($from !== null) {
|
|
|
$query->where('created_at', '>=', $from->format('Y-m-d H:i:s'));
|
|
$query->where('created_at', '>=', $from->format('Y-m-d H:i:s'));
|
|
@@ -75,92 +44,28 @@ class ThirdGameBalanceUsers extends Command
|
|
|
$query->where('created_at', '<=', $to->format('Y-m-d H:i:s'));
|
|
$query->where('created_at', '<=', $to->format('Y-m-d H:i:s'));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $memberIds = array_values(array_filter(array_map('strval', (array) $this->option('member_id'))));
|
|
|
|
|
- if ($memberIds !== []) {
|
|
|
|
|
- $query->whereIn('member_id', $memberIds);
|
|
|
|
|
|
|
+ $specifiedMemberIds = array_values(array_filter(
|
|
|
|
|
+ array_map('strval', (array) $this->option('member_id'))
|
|
|
|
|
+ ));
|
|
|
|
|
+ if ($specifiedMemberIds !== []) {
|
|
|
|
|
+ $query->whereIn('member_id', $specifiedMemberIds);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $rows = $query->groupBy('member_id')->orderBy('member_id')->get();
|
|
|
|
|
- $verifyProvider = !$this->option('skip-provider');
|
|
|
|
|
- $progress = null;
|
|
|
|
|
- if ($verifyProvider && $rows->isNotEmpty()) {
|
|
|
|
|
- $this->info('正在逐个查询三方当前余额……');
|
|
|
|
|
- $progress = $this->output->createProgressBar($rows->count());
|
|
|
|
|
- $progress->start();
|
|
|
|
|
|
|
+ $memberIds = $query->distinct()->orderBy('member_id')->pluck('member_id');
|
|
|
|
|
+ $this->line("member_id\tthird_game_id\tthird_game_balance");
|
|
|
|
|
+ $count = 0;
|
|
|
|
|
+ foreach ($memberIds as $memberId) {
|
|
|
|
|
+ $memberId = (string) $memberId;
|
|
|
|
|
+ $detail = $service->detail($memberId, true);
|
|
|
|
|
+ $balance = ($detail['ok'] ?? false)
|
|
|
|
|
+ ? $this->decimal($detail['total_game_balance'] ?? 0)
|
|
|
|
|
+ : '查询失败:' . (string) ($detail['msg'] ?? '未知错误');
|
|
|
|
|
+ $this->line(implode("\t", [$memberId, $service->playerId($memberId), $balance]));
|
|
|
|
|
+ $count++;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $report = [];
|
|
|
|
|
- foreach ($rows as $row) {
|
|
|
|
|
- $transferInCount = (int) $row->transfer_in_count;
|
|
|
|
|
- $refundCount = (int) $row->refund_count;
|
|
|
|
|
- $transferOutCount = (int) $row->transfer_out_count;
|
|
|
|
|
- $flowTypes = [];
|
|
|
|
|
- if ($transferInCount > 0) {
|
|
|
|
|
- $flowTypes[] = '转入';
|
|
|
|
|
- }
|
|
|
|
|
- if ($refundCount > 0) {
|
|
|
|
|
- $flowTypes[] = '失败退款';
|
|
|
|
|
- }
|
|
|
|
|
- if ($transferOutCount > 0) {
|
|
|
|
|
- $flowTypes[] = '转出';
|
|
|
|
|
- }
|
|
|
|
|
- $item = [
|
|
|
|
|
- 'member_id' => (string) $row->member_id,
|
|
|
|
|
- 'player_id' => $service->playerId((string) $row->member_id),
|
|
|
|
|
- 'flow_type' => implode('+', $flowTypes),
|
|
|
|
|
- 'transfer_in_count' => $transferInCount,
|
|
|
|
|
- 'transfer_in_total' => $this->decimal($row->transfer_in_total),
|
|
|
|
|
- 'refund_count' => $refundCount,
|
|
|
|
|
- 'refund_total' => $this->decimal($row->refund_total),
|
|
|
|
|
- 'transfer_out_count' => $transferOutCount,
|
|
|
|
|
- 'transfer_out_total' => $this->decimal($row->transfer_out_total),
|
|
|
|
|
- 'first_at' => (string) $row->first_at,
|
|
|
|
|
- 'last_at' => (string) $row->last_at,
|
|
|
|
|
- ];
|
|
|
|
|
-
|
|
|
|
|
- if ($verifyProvider) {
|
|
|
|
|
- $detail = $service->detail((string) $row->member_id, true);
|
|
|
|
|
- $item['provider_status'] = ($detail['ok'] ?? false) ? '成功' : '失败';
|
|
|
|
|
- $item['provider_total_balance'] = ($detail['ok'] ?? false)
|
|
|
|
|
- ? $this->decimal($detail['total_game_balance'] ?? 0)
|
|
|
|
|
- : '';
|
|
|
|
|
- $item['provider_balances'] = ($detail['ok'] ?? false)
|
|
|
|
|
- ? $this->providerBalances($detail['game_list'] ?? [])
|
|
|
|
|
- : '';
|
|
|
|
|
- $item['provider_message'] = ($detail['ok'] ?? false)
|
|
|
|
|
- ? ''
|
|
|
|
|
- : (string) ($detail['msg'] ?? '查询失败');
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $report[] = $item;
|
|
|
|
|
- if ($progress !== null) {
|
|
|
|
|
- $progress->advance();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if ($progress !== null) {
|
|
|
|
|
- $progress->finish();
|
|
|
|
|
- $this->newLine(2);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $csvPath = (string) $this->option('csv');
|
|
|
|
|
- if ($csvPath === '') {
|
|
|
|
|
- $csvPath = storage_path('app/third_game_balance_users_' . now()->format('Ymd_His') . '.csv');
|
|
|
|
|
- }
|
|
|
|
|
- if (!$this->writeCsv($csvPath, $report)) {
|
|
|
|
|
- return self::FAILURE;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $show = max(0, (int) $this->option('show'));
|
|
|
|
|
- if ($show > 0 && $report !== []) {
|
|
|
|
|
- $this->table(array_keys($report[0]), array_slice($report, 0, $show));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $this->info('共找到 ' . count($report) . ' 个玩家');
|
|
|
|
|
- $this->info('CSV:' . $csvPath);
|
|
|
|
|
- $this->comment($verifyProvider
|
|
|
|
|
- ? '本命令只读:已请求三方当前余额,未回收余额,未修改钱包或流水。'
|
|
|
|
|
- : '本命令只读:未请求三方接口,未修改钱包或流水。');
|
|
|
|
|
|
|
+ $this->info('共找到 ' . $count . ' 个玩家');
|
|
|
|
|
+ $this->comment('本命令只读:已请求三方当前余额,未回收余额,未修改钱包或流水。');
|
|
|
return self::SUCCESS;
|
|
return self::SUCCESS;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -182,48 +87,4 @@ class ThirdGameBalanceUsers extends Command
|
|
|
{
|
|
{
|
|
|
return bcadd(is_numeric($value) ? (string) $value : '0', '0', 10);
|
|
return bcadd(is_numeric($value) ? (string) $value : '0', '0', 10);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- private function writeCsv(string $path, array $rows): bool
|
|
|
|
|
- {
|
|
|
|
|
- try {
|
|
|
|
|
- File::ensureDirectoryExists(dirname($path));
|
|
|
|
|
- $handle = fopen($path, 'wb');
|
|
|
|
|
- if ($handle === false) {
|
|
|
|
|
- throw new \RuntimeException('无法创建 CSV');
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fwrite($handle, "\xEF\xBB\xBF");
|
|
|
|
|
- $headers = $rows !== [] ? array_keys($rows[0]) : [
|
|
|
|
|
- 'member_id', 'player_id', 'flow_type', 'transfer_in_count', 'transfer_in_total',
|
|
|
|
|
- 'refund_count', 'refund_total', 'transfer_out_count', 'transfer_out_total',
|
|
|
|
|
- 'first_at', 'last_at', 'provider_status', 'provider_total_balance',
|
|
|
|
|
- 'provider_balances', 'provider_message',
|
|
|
|
|
- ];
|
|
|
|
|
- fputcsv($handle, $headers);
|
|
|
|
|
- foreach ($rows as $row) {
|
|
|
|
|
- fputcsv($handle, $row);
|
|
|
|
|
- }
|
|
|
|
|
- fclose($handle);
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (\Throwable $e) {
|
|
|
|
|
- if (isset($handle) && is_resource($handle)) {
|
|
|
|
|
- fclose($handle);
|
|
|
|
|
- }
|
|
|
|
|
- $this->error('写入 CSV 失败:' . $e->getMessage());
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private function providerBalances(array $balances): string
|
|
|
|
|
- {
|
|
|
|
|
- $result = [];
|
|
|
|
|
- foreach ($balances as $balance) {
|
|
|
|
|
- if (!is_array($balance) || !isset($balance['platform']) || !is_numeric($balance['balance'] ?? null)) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- $result[(string) $balance['platform']] = $this->decimal($balance['balance']);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return (string) json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|