User.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <?php
  2. namespace App\Models;
  3. use App\Casts\data_rate;
  4. use App\Casts\money;
  5. use App\Utils\Helpers;
  6. use App\Utils\QQInfo;
  7. use DB;
  8. use Hash;
  9. use Illuminate\Database\Eloquent\Builder;
  10. use Illuminate\Database\Eloquent\Factories\HasFactory;
  11. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  12. use Illuminate\Database\Eloquent\Relations\BelongsToMany;
  13. use Illuminate\Database\Eloquent\Relations\HasMany;
  14. use Illuminate\Database\Eloquent\Relations\HasManyThrough;
  15. use Illuminate\Database\Eloquent\Relations\HasOne;
  16. use Illuminate\Foundation\Auth\User as Authenticatable;
  17. use Illuminate\Notifications\Notifiable;
  18. use Kyslik\ColumnSortable\Sortable;
  19. use Laravel\Sanctum\HasApiTokens;
  20. use Spatie\Permission\Traits\HasRoles;
  21. /**
  22. * 用户信息.
  23. */
  24. class User extends Authenticatable
  25. {
  26. use HasApiTokens, HasFactory, HasRoles, Notifiable, Sortable;
  27. public array $sortable = ['id', 'credit', 'port', 't', 'expired_at'];
  28. protected $table = 'user';
  29. protected $casts = ['credit' => money::class, 'speed_limit' => data_rate::class, 'expired_at' => 'date:Y-m-d', 'reset_time' => 'date:Y-m-d', 'ban_time' => 'date:Y-m-d'];
  30. protected $guarded = [];
  31. public function routeNotificationForMail($notification): string
  32. {
  33. return $this->username;
  34. }
  35. public function usedTrafficPercentage(): float
  36. {
  37. return round($this->used_traffic / $this->transfer_enable, 2);
  38. }
  39. public function getUsedTrafficAttribute(): int
  40. {
  41. return $this->d + $this->u;
  42. }
  43. public function getExpirationDateAttribute(): ?string
  44. {
  45. return $this->attributes['expired_at'];
  46. }
  47. public function getResetDateAttribute(): ?string
  48. {
  49. return $this->attributes['reset_time'];
  50. }
  51. public function getTelegramUserIdAttribute(): ?string
  52. {
  53. $telegram = $this->userAuths()->whereType('telegram')->first();
  54. return $telegram->identifier ?? null;
  55. }
  56. public function userAuths(): HasMany
  57. {
  58. return $this->hasMany(UserOauth::class);
  59. }
  60. public function onlineIpLogs(): HasMany
  61. {
  62. return $this->hasMany(NodeOnlineIp::class);
  63. }
  64. public function payments(): HasMany
  65. {
  66. return $this->hasMany(Payment::class);
  67. }
  68. public function commissionSettlements(): HasMany
  69. {
  70. return $this->hasMany(ReferralApply::class);
  71. }
  72. public function commissionLogs(): HasMany
  73. {
  74. return $this->hasMany(ReferralLog::class, 'inviter_id');
  75. }
  76. public function ruleLogs(): HasMany
  77. {
  78. return $this->hasMany(RuleLog::class);
  79. }
  80. public function tickets(): HasMany
  81. {
  82. return $this->hasMany(Ticket::class);
  83. }
  84. public function ticketReplies(): HasMany
  85. {
  86. return $this->hasMany(TicketReply::class);
  87. }
  88. public function banedLogs(): HasMany
  89. {
  90. return $this->hasMany(UserBanedLog::class);
  91. }
  92. public function creditLogs(): HasMany
  93. {
  94. return $this->hasMany(UserCreditLog::class);
  95. }
  96. public function dailyDataFlows(): HasMany
  97. {
  98. return $this->hasMany(UserDailyDataFlow::class);
  99. }
  100. public function dataFlowLogs(): HasMany
  101. {
  102. return $this->hasMany(UserDataFlowLog::class);
  103. }
  104. public function dataModifyLogs(): HasMany
  105. {
  106. return $this->hasMany(UserDataModifyLog::class);
  107. }
  108. public function hourlyDataFlows(): HasMany
  109. {
  110. return $this->HasMany(UserHourlyDataFlow::class);
  111. }
  112. public function loginLogs(): HasMany
  113. {
  114. return $this->HasMany(UserLoginLog::class);
  115. }
  116. public function subscribe(): HasOne
  117. {
  118. return $this->hasOne(UserSubscribe::class);
  119. }
  120. public function subUrl(): string
  121. {
  122. return route('sub', $this->subscribe->code);
  123. }
  124. public function subscribeLogs(): HasManyThrough
  125. {
  126. return $this->hasManyThrough(UserSubscribeLog::class, UserSubscribe::class);
  127. }
  128. public function verify(): HasMany
  129. {
  130. return $this->hasMany(Verify::class);
  131. }
  132. public function inviter(): BelongsTo
  133. {
  134. return $this->belongsTo(__CLASS__);
  135. }
  136. public function invites(): HasMany
  137. {
  138. return $this->hasMany(Invite::class, 'inviter_id');
  139. }
  140. public function invitees(): HasMany
  141. {
  142. return $this->hasMany(__CLASS__, 'inviter_id');
  143. }
  144. public function getLevelNameAttribute(): string
  145. {
  146. return Level::whereLevel($this->level)->first()->name;
  147. }
  148. public function getCreditTagAttribute(): string
  149. {
  150. return Helpers::getPriceTag($this->credit);
  151. }
  152. public function getTransferEnableFormattedAttribute(): string
  153. {
  154. return formatBytes($this->attributes['transfer_enable']);
  155. }
  156. public function setPasswordAttribute(string $password): string
  157. {
  158. return $this->attributes['password'] = Hash::make($password);
  159. }
  160. public function getAvatarAttribute(): string
  161. {
  162. if ($this->qq) {
  163. $url = QQInfo::getQQAvatar($this->qq);
  164. } elseif (stripos(strtolower($this->username), '@qq.com') !== false) {
  165. $url = QQInfo::getQQAvatar($this->username);
  166. } else {
  167. // $url = 'https://gravatar.loli.net/avatar/'.md5(strtolower(trim($this->username)))."?&d=identicon";
  168. // $url = 'https://robohash.org/'.md5(strtolower(trim($this->username))).'?set=set4&bgset=bg2&size=400x400';
  169. // $url = 'https://api.dicebear.com/6.x/thumbs/svg?seed='.$this->username.'&radius=50';
  170. $url = 'https://api.btstu.cn/sjtx/api.php?lx=c1&format=images&method=zsy';
  171. }
  172. return $url;
  173. }
  174. public function scopeActiveUser(Builder $query): Builder
  175. {
  176. return $query->where('status', '<>', -1)->whereEnable(1);
  177. }
  178. public function scopeBannedUser(Builder $query): Builder
  179. {
  180. return $query->where('status', '<>', -1)->whereEnable(0);
  181. }
  182. public function nodes(int $userLevel = null, int $userGroupId = null): Node|Builder|BelongsToMany
  183. {
  184. if ($userGroupId === null && $this->user_group_id) { // 使用默认的用户分组
  185. $query = $this->userGroup->nodes();
  186. } elseif ($userGroupId) { // 使用给的用户分组
  187. $query = UserGroup::findOrFail($userGroupId)->nodes();
  188. } else { // 无用户分组
  189. $query = Node::query();
  190. }
  191. return $query->whereStatus(1)->where('level', '<=', $userLevel ?? $this->level ?? 0);
  192. }
  193. public function userGroup(): BelongsTo
  194. {
  195. return $this->belongsTo(UserGroup::class);
  196. }
  197. public function getIsAvailableAttribute(): bool
  198. {
  199. return ! $this->ban_time && $this->transfer_enable && $this->expired_at > time();
  200. }
  201. public function updateCredit(float $credit): bool
  202. {
  203. $this->credit += $credit;
  204. return $this->credit >= 0 && $this->save();
  205. }
  206. public function incrementData(int $data): bool
  207. { // 添加用户流量
  208. $this->transfer_enable += $data;
  209. return $this->save();
  210. }
  211. public function isNotCompleteOrderByUserId(int $userId): bool
  212. {
  213. return Order::uid($userId)->whereIn('status', [0, 1])->exists();
  214. }
  215. public function trafficFetch(int $u, int $d): bool
  216. {
  217. $this->u += $u;
  218. $this->d += $d;
  219. return $this->save();
  220. }
  221. public function expiration_status(): int
  222. {
  223. $today = date('Y-m-d');
  224. $nextMonth = date('Y-m-d', strtotime('next month'));
  225. if ($this->expiration_date < $today) {
  226. $status = 0; // 已过期
  227. } elseif ($this->expiration_date === $today) {
  228. $status = 1; // 今日过期
  229. } elseif ($this->expiration_date <= $nextMonth) {
  230. $status = 2; // 一个月内过期
  231. }
  232. return $status ?? 3;
  233. }
  234. public function isTrafficWarning(): bool
  235. { // 流量异常警告
  236. return ((int) sysConfig('traffic_ban_value') * GiB) <= $this->recentTrafficUsed();
  237. }
  238. public function recentTrafficUsed()
  239. {
  240. return UserHourlyDataFlow::userRecentUsed($this->id)->sum(DB::raw('u + d'));
  241. }
  242. public function orders(): HasMany
  243. {
  244. return $this->hasMany(Order::class);
  245. }
  246. public function routeNotificationForTelegram()
  247. {
  248. return $this->telegram_user_id;
  249. }
  250. }