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.

109 lines
3.6 KiB

1 week ago
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Services\VenueImportService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Component\HttpFoundation\StreamedResponse;
class VenueImportController extends Controller
{
public function __construct(
private VenueImportService $venueImportService
) {}
public function template(Request $request): StreamedResponse
{
$user = $request->user();
abort_unless($user?->isSuperAdmin(), 403, '仅超级管理员可下载导入模板');
$spreadsheet = $this->venueImportService->buildTemplateSpreadsheet();
$writer = new Xlsx($spreadsheet);
return response()->streamDownload(function () use ($writer) {
$writer->save('php://output');
}, '场馆导入模板.xlsx', [
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
]);
}
public function preview(Request $request): JsonResponse
{
abort_unless($request->user()?->isSuperAdmin(), 403, '仅超级管理员可导入场馆');
3 days ago
// 使用 extensions 而非 mimesmimes 依赖 fileinfoguessExtension部分环境未启用会报错
1 week ago
$request->validate([
3 days ago
'file' => ['required', 'file', 'extensions:xlsx,xls', 'max:10240'],
1 week ago
]);
$path = $request->file('file')->getRealPath();
if ($path === false) {
return response()->json(['message' => '无法读取上传文件'], 422);
}
$result = $this->venueImportService->previewFromPath($path);
return response()->json($result);
}
public function confirm(Request $request): JsonResponse
{
$user = $request->user();
abort_unless($user?->isSuperAdmin(), 403, '仅超级管理员可导入场馆');
$data = $request->validate([
'rows' => ['required', 'array', 'min:1'],
'rows.*' => ['required', 'array'],
]);
$created = [];
1 day ago
$updated = [];
1 week ago
$errors = [];
DB::beginTransaction();
try {
foreach ($data['rows'] as $index => $row) {
$errs = $this->venueImportService->validatePayload($row);
if ($errs !== []) {
$errors[] = ['index' => $index, 'errors' => $errs];
continue;
}
$clean = $this->venueImportService->stripInternalKeys($row);
1 day ago
$result = $this->venueImportService->importRowFromPayload($clean, $user);
$rowArr = $result['venue']->toArray();
if ($result['action'] === 'updated') {
$updated[] = $rowArr;
} else {
$created[] = $rowArr;
}
1 week ago
}
if ($errors !== []) {
DB::rollBack();
return response()->json([
'message' => '部分数据校验失败,未写入任何场馆',
'failures' => $errors,
], 422);
}
DB::commit();
} catch (\Throwable $e) {
DB::rollBack();
throw $e;
}
1 day ago
// 兼容旧版count 为本次成功处理的总行数(含新增+更新)
1 week ago
return response()->json([
'created' => $created,
1 day ago
'updated' => $updated,
'created_count' => count($created),
'updated_count' => count($updated),
'count' => count($created) + count($updated),
1 week ago
], 201);
}
}