User.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Contracts\Auth\MustVerifyEmail;
  4. use Illuminate\Database\Eloquent\Factories\HasFactory;
  5. use Illuminate\Foundation\Auth\User as Authenticatable;
  6. use Illuminate\Notifications\Notifiable;
  7. use Laravel\Sanctum\HasApiTokens;
  8. use Illuminate\Support\Facades\Auth;
  9. use Illuminate\Support\Facades\Hash;
  10. use Illuminate\Support\Facades\DB;
  11. use App\Http\Traits\CommonTrait;
  12. use App\Http\Traits\AgentTrait;
  13. use App\Http\Traits\CryptTrait;
  14. use App\Models\DTO\SearchData;
  15. use App\Notifications\VerifyNotify;
  16. class User extends Authenticatable implements MustVerifyEmail
  17. {
  18. use HasApiTokens, HasFactory, Notifiable, AgentTrait, CommonTrait, CryptTrait;
  19. protected $table = 'users';
  20. protected $primaryKey = 'id';
  21. const CREATED_AT = 'created_at';
  22. const UPDATED_AT = 'updated_at';
  23. const DELETED_AT = 'deleted_at';
  24. /**
  25. * The attributes that are mass assignable.
  26. *
  27. * @var array<int, string>
  28. */
  29. protected $fillable = [
  30. 'user_group_id',
  31. 'user_grade_id',
  32. 'sid',
  33. 'email',
  34. 'name',
  35. 'nickname',
  36. 'password',
  37. 'today_message',
  38. 'group',
  39. 'grade',
  40. 'phone',
  41. 'birthday',
  42. 'gender',
  43. 'icon',
  44. 'thumb',
  45. 'receive_email',
  46. 'receive_sms',
  47. 'receive_note',
  48. 'register_ip',
  49. 'last_login_at',
  50. 'last_login_ip',
  51. 'is_open_profile',
  52. 'is_email_cert',
  53. 'is_auth_cert',
  54. 'is_adult_cert',
  55. 'is_denied',
  56. 'is_admin',
  57. 'is_withdraw',
  58. 'about_me',
  59. 'email_verified_at'
  60. ];
  61. /**
  62. * The attributes that should be hidden for serialization.
  63. *
  64. * @var array<int, string>
  65. */
  66. protected $hidden = [
  67. 'password',
  68. 'remember_token',
  69. ];
  70. /**
  71. * The attributes that should be cast.
  72. *
  73. * @var array<string, string>
  74. */
  75. protected $casts = [
  76. 'last_login_at' => 'datetime', // 마지막 로그인 일시
  77. 'email_verified_at' => 'datetime', // 이메일 인증일시
  78. 'auth_certified_at' => 'datetime', // 본인 인증일시
  79. 'password_updated_at' => 'datetime' // 비밀번호 변경일시
  80. ];
  81. /**
  82. * The attributes that should be mutated to dates.
  83. *
  84. * @var array
  85. */
  86. protected $dates = [
  87. 'last_login_at',
  88. 'created_at',
  89. 'updated_at',
  90. 'deleted_at',
  91. 'email_verified_at',
  92. 'auth_certified_at',
  93. 'password_updated_at'
  94. ];
  95. public function userGroup()
  96. {
  97. return $this->belongsTo(UserGroup::class)->withDefault();
  98. }
  99. public function userGrade()
  100. {
  101. return $this->belongsTo(UserGrade::class)->withDefault();
  102. }
  103. /**
  104. * 회원 비밀번호 조회
  105. */
  106. public function getAuthPassword(): string
  107. {
  108. return $this->password;
  109. }
  110. /**
  111. * 회원 정보 모두 조회
  112. */
  113. public function info()
  114. {
  115. return Auth::user();
  116. }
  117. /**
  118. * 회원 ID로 조회
  119. */
  120. public function findByUserID(string $sid): User
  121. {
  122. $query = $this->query();
  123. $query->where('sid', $sid);
  124. return $query->firstOrNew();
  125. }
  126. /**
  127. * 회원 이메일로 조회
  128. */
  129. public function findByEmail(string $sid): User
  130. {
  131. $query = $this->query();
  132. $query->where('email', $sid);
  133. return $query->firstOrNew();
  134. }
  135. /**
  136. * 최고 관리자 조회
  137. */
  138. public function getMaster(): User
  139. {
  140. return $this->where('is_admin', 1)->findOrNew(config('master_key'));
  141. }
  142. /**
  143. * 운영자 조회
  144. */
  145. public function getAdmins()
  146. {
  147. return $this->where('is_admin', 1)->get();
  148. }
  149. /**
  150. * 회원 ID Unique 생성
  151. */
  152. public function getHashKey(): string
  153. {
  154. return md5(bin2hex($this->getKey()));
  155. }
  156. /**
  157. * 인증 이메일 반환
  158. */
  159. public function getEmailForPasswordReset(): string
  160. {
  161. return $this->email;
  162. }
  163. /**
  164. * 자동 로그인 토큰 이름
  165. */
  166. public function getRememberTokenName(): string
  167. {
  168. return 'remember_token';
  169. }
  170. /**
  171. * 자동 로그인 토큰 조회
  172. */
  173. public function getRememberToken(): ?string
  174. {
  175. return $this->remember_token;
  176. }
  177. /**
  178. * 관리자 여부 확인
  179. */
  180. public function isAdministrator(): bool
  181. {
  182. return boolval($this->is_admin);
  183. }
  184. /**
  185. * 인증받은 이메일 조회
  186. */
  187. public function getEmailForVerification(): string
  188. {
  189. return $this->email;
  190. }
  191. /**
  192. * 이메일 인증을 받았는지 확인
  193. */
  194. public function hasVerifiedEmail(): bool
  195. {
  196. return (config('use_register_email_auth') ? !is_null($this->email_verified_at) : true);
  197. }
  198. /**
  199. * 이메일 인증 완료 후 값 변경
  200. */
  201. public function markEmailAsVerified(): bool
  202. {
  203. return $this->forceFill([
  204. 'is_email_cert' => 1,
  205. 'email_verified_at' => $this->freshTimestamp()
  206. ])->save();
  207. }
  208. /**
  209. * 회원 조회
  210. */
  211. public function data(SearchData $params): object
  212. {
  213. $query = $this->query();
  214. $query->select(
  215. 'users.*'
  216. );
  217. if ($params->keyword) {
  218. switch ($params->field) {
  219. case 'users.id':
  220. case 'users.sid':
  221. $query->where($params->field, '=', $params->keyword);
  222. break;
  223. case 'users.name':
  224. case 'users.email':
  225. $query->where($params->field, 'LIKE', "%{$params->keyword}%");
  226. break;
  227. case 'users.created_at':
  228. case 'users.last_login_at':
  229. $query->whereDate($params->field, $params->keyword);
  230. break;
  231. }
  232. }
  233. if($params->receiveEmail) {
  234. $query->where('users.receive_email', 1);
  235. }
  236. if($params->receiveSms) {
  237. $query->where('users.receive_sms', 1);
  238. }
  239. if($params->receiveNote) {
  240. $query->where('users.receive_note',1);
  241. }
  242. if ($params->isAdmin) {
  243. $query->where('users.is_admin', '=', $params->isAdmin);
  244. }
  245. if ($params->isDenied) {
  246. $query->where('users.is_denied', '=', $params->isDenied);
  247. }
  248. if ($params->isWithdraw) {
  249. $query->where('users.is_withdraw', '=', $params->isWithdraw);
  250. }
  251. $query->orderByDesc('users.id');
  252. $list = $query->paginate($params->perPage, ['*'], 'page', $params->page);
  253. $total = $this->count();
  254. $rows = $list->count();
  255. return (object)[
  256. 'total' => $total,
  257. 'rows' => $rows,
  258. 'list' => $list
  259. ];
  260. }
  261. /**
  262. * 휴면회원 조회
  263. */
  264. public function dormantUsers(SearchData $params): object
  265. {
  266. $query = $this->query();
  267. $query->select(
  268. 'users.*'
  269. );
  270. if ($params->keyword) {
  271. switch ($params->field) {
  272. case 'users.id':
  273. case 'users.sid':
  274. $query->where($params->field, '=', $params->keyword);
  275. break;
  276. case 'users.name':
  277. case 'users.email':
  278. $query->where($params->field, 'LIKE', "%{$params->keyword}%");
  279. break;
  280. case 'users.created_at':
  281. case 'users.last_login_at':
  282. $query->whereDate($params->field, $params->keyword);
  283. break;
  284. }
  285. }
  286. $query->whereNotBetween('last_login_at', [
  287. now()->subYears(1)->format('Y-m-d 00:00:00'),
  288. now()->format('Y-m-d 23:59:59')
  289. ]);
  290. $query->orderByDesc('users.created_at');
  291. $list = $query->paginate($params->perPage, ['*'], 'page', $params->page);
  292. $total = $this->count();
  293. $rows = $list->count();
  294. return (object)[
  295. 'total' => $total,
  296. 'rows' => $rows,
  297. 'list' => $list
  298. ];
  299. }
  300. /**
  301. * 회원가입
  302. */
  303. public function register(array $data): mixed
  304. {
  305. return DB::transaction(function () use ($data)
  306. {
  307. $sid = current(explode('@', $data['email']));
  308. $ipAddress = request()->getClientIp();
  309. $userAgent = request()->headers->get('user-agent');
  310. $referer = request()->headers->get('referer');
  311. $device = $this->device($userAgent);
  312. $browser = $this->browser($userAgent);
  313. $platform = $this->platform($userAgent);
  314. $robot = $this->robot($userAgent);
  315. $language = $this->languages();
  316. $user = User::create([
  317. 'sid' => $sid,
  318. 'email' => $data['email'],
  319. 'name' => null,
  320. 'nickname' => $data['nickname'],
  321. 'phone' => null,
  322. 'birthday' => null,
  323. 'gender' => null,
  324. 'nationality' => null,
  325. 'password' => Hash::make($data['password']),
  326. 'register_ip' => $ipAddress,
  327. 'ci' => null,
  328. 'updated_at' => null,
  329. 'is_auth_cert' => 1,
  330. 'is_adult_cert' => 0,
  331. 'auth_certified_at' => null
  332. ]);
  333. UserRegister::create([
  334. 'user_id' => $user->id,
  335. 'device' => $device,
  336. 'language' => $language,
  337. 'browser' => $browser,
  338. 'platform' => $platform,
  339. 'robot' => $robot,
  340. 'ip_address' => $ipAddress,
  341. 'user_agent' => $userAgent,
  342. 'referer' => $referer
  343. ]);
  344. return $user;
  345. });
  346. }
  347. /**
  348. * 회원 정보 수정
  349. */
  350. public function updater(int $uid, array $data): bool
  351. {
  352. return $this->find($uid)->update($data);
  353. }
  354. /**
  355. * 중복 이메일 확인
  356. */
  357. public function sendVerifyCodeToMail(): void
  358. {
  359. $this->notify(new VerifyNotify);
  360. }
  361. /**
  362. * 이메일 인증 정보 변경
  363. */
  364. public function generateEmailVerified(string $email): void
  365. {
  366. [$sid] = explode('@', $email);
  367. $this->sid = $sid;
  368. $this->email = $email;
  369. $this->email_verified_at = now();
  370. $this->is_email_cert = 1;
  371. $this->save();
  372. $this->sendVerifyCodeToMail();
  373. }
  374. /**
  375. * 회원 포인트(P)를 최신 상태로 변경
  376. */
  377. public function updateTotalPoint(int $userID): bool
  378. {
  379. $sql = "
  380. UPDATE users USR
  381. SET USR.point = (SELECT COALESCE(SUM(P.point), 0) FROM tb_point P WHERE P.user_id = USR.id)
  382. WHERE USR.id = ?;
  383. ";
  384. return DB::statement($sql, [$userID]);
  385. }
  386. /**
  387. * 토스 인증 CI로 회원 정보를 조회
  388. */
  389. public function findUserByTossCI(string $ci): User
  390. {
  391. return $this->where('ci', $ci)->firstOrNew();
  392. }
  393. /**
  394. * 성인여부 확인 (생년월일로 확인)
  395. */
  396. public function isAdult(): bool
  397. {
  398. return boolval(now()->diffInYears($this->birthday) > 19);
  399. }
  400. /**
  401. * 회원가입 일별통계
  402. */
  403. public function dailyStats(): array
  404. {
  405. $sql = '
  406. SELECT DATE(`created_at`) AS `date`, COUNT(*) AS `value` FROM users GROUP BY `created_at`;
  407. ';
  408. return DB::select($sql);
  409. }
  410. /**
  411. * 주간 회원가입자 수
  412. */
  413. public function totalWeeklyCount(): int
  414. {
  415. $sql = '
  416. SELECT COUNT(*) AS `count` FROM users WHERE WEEK(`created_at`) = WEEK(CURRENT_TIMESTAMP);
  417. ';
  418. return DB::selectOne($sql)->count;
  419. }
  420. /**
  421. * 월별 회원가입자 수
  422. */
  423. public function totalMonthlyCount(): int
  424. {
  425. $sql = '
  426. SELECT COUNT(*) AS `count` FROM users WHERE MONTH(`created_at`) = MONTH(CURRENT_TIMESTAMP);
  427. ';
  428. return DB::selectOne($sql)->count;
  429. }
  430. /**
  431. * 전체 회원가입자 수
  432. */
  433. public function totalUserCount(): int
  434. {
  435. return $this->where('is_withdraw', 0)->count();
  436. }
  437. }