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

1 week ago
<?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);
}
}