You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
4.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?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 : 只计算并导出,不写入数据库}';
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);
}
$this->writeXlsx($outPath, $rows);
$this->info("Excel 已生成:{$outPath}");
if ($this->option('dry-run')) {
$this->warn('dry-run未写入用户数据。');
return self::SUCCESS;
}
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).' 个场馆管理员账号并完成场馆绑定。');
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);
}
}