|
|
<?php
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
|
use App\Models\User;
|
|
|
use App\Models\Venue;
|
|
|
use App\Support\VenueAdminCredentials;
|
|
|
use Illuminate\Console\Command;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
|
|
|
|
class BatchCreateVenueAdminAccountsCommand extends Command
|
|
|
{
|
|
|
protected $signature = 'venues:create-admin-accounts
|
|
|
{--output= : 导出 xlsx 绝对或相对路径(默认 storage/app/exports/venue_admins_时间戳.xlsx)}
|
|
|
{--dry-run : 仅生成 Excel,不向 users / user_venue 写入(预览或离线存档)}';
|
|
|
|
|
|
protected $description = '为每个场馆创建一个场馆管理员账号(姓名=馆名,用户名=拼音首字母缩写,密码按规则),并绑定该场馆;导出含明文密码的 Excel';
|
|
|
|
|
|
public function handle(): int
|
|
|
{
|
|
|
$venues = Venue::query()->orderBy('id')->get(['id', 'name']);
|
|
|
if ($venues->isEmpty()) {
|
|
|
$this->warn('数据库中暂无场馆。');
|
|
|
|
|
|
return self::FAILURE;
|
|
|
}
|
|
|
|
|
|
$this->info("将处理 {$venues->count()} 个场馆。");
|
|
|
|
|
|
$rows = [];
|
|
|
$reservedNames = User::query()->pluck('username')->all();
|
|
|
$reservedSet = array_fill_keys($reservedNames, true);
|
|
|
$batchUsed = [];
|
|
|
|
|
|
foreach ($venues as $venue) {
|
|
|
$name = (string) $venue->name;
|
|
|
$base = VenueAdminCredentials::acronymFromVenueName($name);
|
|
|
$plainPassword = VenueAdminCredentials::passwordFromAcronym($base);
|
|
|
|
|
|
$username = $base;
|
|
|
$suffix = 2;
|
|
|
while (isset($reservedSet[$username]) || isset($batchUsed[$username])) {
|
|
|
$username = $base.$suffix;
|
|
|
$suffix++;
|
|
|
}
|
|
|
$batchUsed[$username] = true;
|
|
|
|
|
|
$rows[] = [
|
|
|
'venue_id' => $venue->id,
|
|
|
'name' => $name,
|
|
|
'username' => $username,
|
|
|
'password_plain' => $plainPassword,
|
|
|
'role' => '场馆管理员',
|
|
|
'venue_name' => $name,
|
|
|
];
|
|
|
}
|
|
|
|
|
|
$defaultDir = storage_path('app/exports');
|
|
|
if (! is_dir($defaultDir)) {
|
|
|
mkdir($defaultDir, 0755, true);
|
|
|
}
|
|
|
|
|
|
$outPath = $this->option('output');
|
|
|
if (! $outPath) {
|
|
|
$outPath = $defaultDir.'/venue_admins_'.now()->format('Ymd_His').'.xlsx';
|
|
|
} elseif (! str_starts_with((string) $outPath, '/')) {
|
|
|
$outPath = base_path($outPath);
|
|
|
}
|
|
|
|
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
|
|
|
|
if ($dryRun) {
|
|
|
$this->warn('当前为 --dry-run:只生成 Excel,不会写入 users / user_venue。去掉 --dry-run 才会入库。');
|
|
|
} else {
|
|
|
$this->info('即将写入数据库(users、user_venue),随后生成 Excel…');
|
|
|
DB::transaction(function () use ($rows) {
|
|
|
foreach ($rows as $row) {
|
|
|
$user = User::updateOrCreate(
|
|
|
['username' => $row['username']],
|
|
|
[
|
|
|
'name' => $row['name'],
|
|
|
'email' => null,
|
|
|
'password' => $row['password_plain'],
|
|
|
'role' => 'venue_admin',
|
|
|
'is_active' => true,
|
|
|
]
|
|
|
);
|
|
|
|
|
|
$user->venues()->sync([$row['venue_id']]);
|
|
|
}
|
|
|
});
|
|
|
$this->info('数据库写入完成:已创建/更新 '.count($rows).' 个场馆管理员并完成场馆绑定。');
|
|
|
}
|
|
|
|
|
|
$this->writeXlsx($outPath, $rows);
|
|
|
$this->info("Excel 已生成:{$outPath}");
|
|
|
|
|
|
if ($dryRun) {
|
|
|
$this->warn('本次未写入数据库。若需要入库请执行:php artisan venues:create-admin-accounts(不要带 --dry-run)');
|
|
|
|
|
|
return self::SUCCESS;
|
|
|
}
|
|
|
|
|
|
return self::SUCCESS;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @param array<int, array{venue_id:int, name:string, username:string, password_plain:string, role:string, venue_name:string}> $rows
|
|
|
*/
|
|
|
private function writeXlsx(string $path, array $rows): void
|
|
|
{
|
|
|
$sheetRows = [
|
|
|
['场馆ID', '姓名', '用户名', '密码(明文)', '角色', '绑定场馆'],
|
|
|
];
|
|
|
foreach ($rows as $r) {
|
|
|
$sheetRows[] = [
|
|
|
$r['venue_id'],
|
|
|
$r['name'],
|
|
|
$r['username'],
|
|
|
$r['password_plain'],
|
|
|
$r['role'],
|
|
|
$r['venue_name'],
|
|
|
];
|
|
|
}
|
|
|
|
|
|
$spreadsheet = new Spreadsheet;
|
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
|
$sheet->setTitle('场馆管理员账号');
|
|
|
$sheet->fromArray($sheetRows, null, 'A1');
|
|
|
|
|
|
$writer = new Xlsx($spreadsheet);
|
|
|
$writer->save($path);
|
|
|
}
|
|
|
}
|