2
0

SessionService.php 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. <?php
  2. namespace App\Services\V3;
  3. use App\Models\UserInfo;
  4. use App\Services\AuthService;
  5. use App\Services\UserService;
  6. use Illuminate\Validation\ValidationException;
  7. /**
  8. * 登录与「我是谁」。
  9. *
  10. * 与 v2 的 `AuthController@signIn` / `@getUserInfoByToken` 不共用代码(CLAUDE.md 铁律 2)。
  11. * 签 token 用共享的 `AuthService::getUserToken()`,v2 / v3 签出的 token 完全一样,
  12. * 两边互认——v4 登录拿到的 token 在 v3 也能用,反之亦然。
  13. *
  14. * 与 v2 的区别:
  15. * - 登录一次就返回 token 与用户信息,前端不必再调一次 `auth/current`。
  16. * - 邮箱登录不区分大小写;用户名仍区分(与 user_infos 的唯一索引一致)。
  17. * - 密码比对走 {@see PasswordHasher::check()}(hash_equals);v2 在 SQL 里比 md5。
  18. * - 登录失败统一 422 `errors.login`,不区分「没这个人」与「密码错」。
  19. */
  20. class SessionService
  21. {
  22. public function __construct(
  23. private readonly PasswordHasher $hasher,
  24. private readonly UserService $users,
  25. ) {}
  26. /**
  27. * 用用户名或邮箱 + 密码登录。
  28. *
  29. * @return array{token: string, user: array<string, mixed>}
  30. *
  31. * @throws ValidationException 账号或密码不对
  32. */
  33. public function signIn(string $login, string $password): array
  34. {
  35. $user = UserInfo::where('username', $login)
  36. ->orWhereRaw('lower(email) = ?', [mb_strtolower($login)])
  37. ->first();
  38. if (! $user || ! $this->hasher->check($password, (string) $user->password)) {
  39. throw ValidationException::withMessages(['login' => __('auth.failed')]);
  40. }
  41. $token = AuthService::getUserToken($user->userid);
  42. if (! $token) {
  43. // getUserToken 只在查无此人时返回 null,而上面刚查到——走到这里是数据不一致
  44. abort(500);
  45. }
  46. return [
  47. 'token' => $token,
  48. 'user' => $this->profile($user),
  49. ];
  50. }
  51. /**
  52. * 当前登录用户的资料。token 有效但账号已不存在(被删)时 401。
  53. *
  54. * @return array<string, mixed>
  55. */
  56. public function me(string $userUid): array
  57. {
  58. $user = UserInfo::where('userid', $userUid)->first();
  59. if (! $user) {
  60. abort(401);
  61. }
  62. return $this->profile($user);
  63. }
  64. /**
  65. * 公开摘要(与 v3 其他资源里的 user 同形)+ 本人才需要的补充:
  66. * roles 恒为数组(没有就是 []),root 管理员固定是 ['root']——与 v2 auth/current 一致。
  67. *
  68. * @return array<string, mixed>
  69. */
  70. private function profile(UserInfo $user): array
  71. {
  72. $profile = $this->users->profile($user);
  73. $profile['roles'] = $user->userid === config('mint.admin.root_uuid')
  74. ? ['root']
  75. : (is_array($profile['roles'] ?? null) ? $profile['roles'] : []);
  76. $profile['avatar'] ??= null;
  77. $profile['email'] = $user->email;
  78. return $profile;
  79. }
  80. }