SiteInboxService.php 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. <?php
  2. namespace App\Services;
  3. use App\Support\LocalizedContent;
  4. use Carbon\Carbon;
  5. use Illuminate\Support\Facades\DB;
  6. use Illuminate\Support\Facades\Validator;
  7. use Illuminate\Validation\Rule;
  8. class SiteInboxService
  9. {
  10. public function listing(string $kind, int $id, array $input): array
  11. {
  12. $params = Validator::make($input, [
  13. 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:100'],
  14. 'category' => ['nullable', Rule::in(array_keys(SiteMessageService::CATEGORIES))], 'is_read' => ['nullable', 'integer', 'in:0,1'],
  15. ])->validate();
  16. $query = $this->query($kind, $id);
  17. if (!empty($params['category'])) $query->where('m.category', $params['category']);
  18. if (isset($params['is_read'])) { if ((int) $params['is_read'] === 1) $query->whereNotNull('r.read_at'); else $query->whereNull('r.read_at'); }
  19. $count = (clone $query)->count();
  20. $rows = $query->select('m.id', 'm.category', 'm.sent_at', 't.title', 't.subtitle', 'r.language', 'r.read_at')->orderByDesc('m.sent_at')->orderByDesc('m.id')
  21. ->forPage((int) ($params['page'] ?? 1), (int) ($params['limit'] ?? 20))->get();
  22. return ['count' => $count, 'list' => $rows->map(fn ($row) => $this->dto((array) $row))->all()];
  23. }
  24. public function detail(string $kind, int $recipient, int $id): array
  25. {
  26. $row = $this->query($kind, $recipient)->where('m.id', $id)->select('m.id', 'm.category', 'm.sent_at', 't.title', 't.subtitle', 't.content', 'r.language', 'r.read_at')->first();
  27. if (!$row) LocalizedContent::invalid('id', '信息不存在');
  28. return $this->dto((array) $row);
  29. }
  30. public function unread(string $kind, int $id): array { return ['count' => $this->query($kind, $id)->whereNull('r.read_at')->count()]; }
  31. public function read(string $kind, int $recipient, int $id): array
  32. {
  33. return DB::transaction(function () use ($kind, $recipient, $id) {
  34. $message = DB::table('site_messages')->where('id', $id)->where('status', 'sent')->lockForUpdate()->first();
  35. if (!$message) LocalizedContent::invalid('id', '信息不存在');
  36. $receipt = DB::table('site_message_recipients')->where('message_id', $id)->where('recipient_kind', $kind)->where('recipient_id', $recipient)->lockForUpdate()->first();
  37. if (!$receipt) LocalizedContent::invalid('id', '信息不存在');
  38. $readAt = $receipt->read_at ?? Carbon::now('Asia/Shanghai')->format('Y-m-d H:i:s');
  39. if ($receipt->read_at === null) DB::table('site_message_recipients')->where('id', $receipt->id)->update(['read_at' => $readAt]);
  40. return ['id' => $id, 'is_read' => true, 'read_at' => $readAt];
  41. });
  42. }
  43. private function query(string $kind, int $id)
  44. {
  45. if (!in_array($kind, ['user', 'agent'], true) || $id < 1) LocalizedContent::invalid('recipient', '请先登录');
  46. return DB::table('site_message_recipients as r')->join('site_messages as m', 'm.id', '=', 'r.message_id')
  47. ->join('site_message_translations as t', function ($j) { $j->on('t.message_id', '=', 'r.message_id')->on('t.language', '=', 'r.language'); })
  48. ->where('r.recipient_kind', $kind)->where('r.recipient_id', $id)->where('m.status', 'sent');
  49. }
  50. private function dto(array $row): array { $row['id'] = (int) $row['id']; $row['category_name'] = SiteMessageService::CATEGORIES[$row['category']]; $row['is_read'] = $row['read_at'] !== null; return $row; }
  51. }