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.

145 lines
4.9 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\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
class AuthController extends Controller
{
public function login(Request $request): JsonResponse
{
$data = $request->validate([
'username' => ['required', 'string'],
'password' => ['required', 'string'],
]);
$user = User::with('venues:id,name')->where('username', $data['username'])->first();
if (! $user || ! $user->is_active || ! Hash::check($data['password'], $user->password)) {
$this->recordLoginAudit(
$request,
null,
422,
'登录失败(账号或密码错误)',
['username' => $data['username']]
);
return response()->json(['message' => '账号或密码错误'], 422);
}
// 移动端核销登录:签发更长有效期的 tokenSanctum 仍会在 expires_at 到期后失效)
$isH5Verify = $request->input('client') === 'h5_verify';
if ($isH5Verify && ! $user->isSuperAdmin()) {
$this->recordLoginAudit(
$request,
$user,
403,
'登录失败(核销端仅限超级管理员账号)',
['username' => $data['username'], 'client' => 'h5_verify']
);
return response()->json(['message' => '场馆管理员请使用活动专用核销链接与账号登录'], 403);
}
$expiresAt = $isH5Verify ? now()->addMonths(6) : null;
$tokenName = $isH5Verify ? 'h5-verify' : 'admin-token';
$token = $user->createToken($tokenName, ['*'], $expiresAt)->plainTextToken;
$this->recordLoginAudit(
$request,
$user,
200,
'登录('.$user->username.'',
[
'username' => $data['username'],
'client' => $request->input('client'),
]
);
return response()->json([
'token' => $token,
'user' => [
'id' => $user->id,
'username' => $user->username,
'name' => $user->name,
'role' => $user->role,
'venues' => $user->venues,
'full_admin_access' => $user->isSuperAdmin(),
],
]);
}
public function logout(Request $request): JsonResponse
{
$request->user()?->currentAccessToken()?->delete();
return response()->json(['message' => '已退出登录']);
}
public function updateProfile(Request $request): JsonResponse
{
$user = $request->user();
if (! $user instanceof User) {
return response()->json(['message' => '用户不存在'], 404);
}
$data = $request->validate([
'name' => ['required', 'string', 'max:100'],
'password' => [
'nullable',
'string',
Password::min(8)
->letters()
->mixedCase()
->symbols(),
],
], [
'password.min' => '密码长度至少 8 位',
'password.letters' => '密码需包含字母',
'password.mixed' => '密码需包含大小写字母',
'password.symbols' => '密码需包含特殊字符',
]);
$user->name = trim((string) $data['name']);
if (isset($data['password']) && trim((string) $data['password']) !== '') {
$user->password = Hash::make((string) $data['password']);
}
$user->save();
return response()->json([
'id' => $user->id,
'username' => $user->username,
'name' => $user->name,
'role' => $user->role,
]);
}
/**
* @param array<string, mixed> $payload
*/
private function recordLoginAudit(Request $request, ?User $user, int $statusCode, string $operationSummary, array $payload = []): void
{
try {
AuditLog::create([
'user_id' => $user?->id,
'username' => $user?->username ?? (isset($payload['username']) ? (string) $payload['username'] : null),
'role' => $user?->role,
'method' => 'POST',
'path' => '/'.ltrim($request->path(), '/'),
'action' => 'POST '.$request->path(),
'operation_summary' => $operationSummary,
'status_code' => $statusCode,
'ip' => $request->ip(),
'user_agent' => substr((string) $request->userAgent(), 0, 500),
'request_payload' => $payload,
]);
} catch (\Throwable) {
}
}
}