Browse Source

Merge branch 'message-notification' into dev

moell 11 months ago
parent
commit
f0ce9c771c

+ 94 - 0
app/Http/Controllers/API/NotificationController.php

@@ -0,0 +1,94 @@
+<?php
+
+namespace App\Http\Controllers\API;
+
+use App\Http\Controllers\Controller;
+use App\Http\Resources\API\NotificationResource;
+use App\Models\Enums\NotificationStatus;
+use App\Models\Notification;
+use App\Models\NotificationRecord;
+use Carbon\Carbon;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+class NotificationController extends Controller
+{
+    /**
+     * 用户通知列表
+     *
+     * @param Request $request
+     * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
+     */
+    public function index(Request $request)
+    {
+        $notifications = Notification::query()
+            ->filter($request->all())
+            ->join("notification_records", "notifications.id", "=", "notification_records.notification_id")
+            ->where("notification_records.user_id", Auth::id())
+            ->selectRaw("notifications.*,notification_records.read_at")
+            ->orderByDesc("created_at")
+            ->paginate();
+
+        return NotificationResource::collection($notifications);
+    }
+
+    /**
+     * 标记为已读
+     *
+     * @param Request $request
+     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
+     */
+    public function markAsRead(Request $request)
+    {
+        $ids = $request->get("ids");
+        if (! $ids) {
+            return $this->badRequest("Data is empty");
+        }
+
+        NotificationRecord::query()
+            ->where("user_id", Auth::id())
+            ->whereIn("notification_id", $ids)
+            ->whereNull("read_at")
+            ->update([
+                'read_at' => Carbon::now(),
+            ]);
+
+        return $this->noContent();
+    }
+
+    /**
+     * 未读消息
+     *
+     * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
+     */
+    public function unread()
+    {
+        $announcements = Notification::query()
+            ->leftJoin("notification_records", "notifications.id", "=", "notification_records.notification_id")
+            ->where("notification_records.user_id", Auth::id())
+            ->where("notifications.status", NotificationStatus::RELEASE)
+            ->where("notifications.start_at", "<", Carbon::now())
+            ->where(fn($query) => $query->where("notifications.end_at", ">", Carbon::now())->orWhereNull("notifications.end_at"))
+            ->whereNull("notification_records.id")
+            ->selectRaw("notifications.id")
+            ->get();
+
+        foreach ($announcements as $announcement) {
+            NotificationRecord::query()->firstOrCreate([
+                'notification_id' => $announcement->id,
+                'user_id' => Auth::id()
+            ]);
+        }
+
+
+        $notifications = Notification::query()
+            ->join("notification_records", "notifications.id", "=", "notification_records.notification_id")
+            ->where("notification_records.user_id", Auth::id())
+            ->whereNull("notification_records.read_at")
+            ->selectRaw("notifications.*,notification_records.read_at")
+            ->orderByDesc("created_at")
+            ->paginate();
+
+        return NotificationResource::collection($notifications);
+    }
+}

+ 25 - 0
app/Http/Resources/API/NotificationResource.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Http\Resources\API;
+
+use Illuminate\Http\Request;
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class NotificationResource extends JsonResource
+{
+    /**
+     * Transform the resource into an array.
+     *
+     * @return array<string, mixed>
+     */
+    public function toArray(Request $request): array
+    {
+        return [
+            'id' => $this->id,
+            'content' => $this->content,
+            'created_at' => (string)$this->created_at,
+            'read_at' => (string)$this->read_at,
+            'read_status' => (bool)$this->read_at,
+        ];
+    }
+}

+ 43 - 0
app/ModelFilters/NotificationFilter.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace App\ModelFilters;
+
+use EloquentFilter\ModelFilter;
+
+class NotificationFilter extends ModelFilter
+{
+    /**
+    * Related Models that have ModelFilters as well as the method on the ModelFilter
+    * As [relationMethod => [input_key1, input_key2]].
+    *
+    * @var array
+    */
+    public $relations = [];
+
+    public function startReadAt($date)
+    {
+        return $this->where("notification_records.read_at", ">=", $date);
+    }
+
+    public function endReadAt($date)
+    {
+        return $this->where("notification_records.read_at", "<", $date);
+    }
+
+    public function startNotificationAt($date)
+    {
+        return $this->where("notifications.created_at", ">=", $date);
+    }
+
+    public function endNotificationAt($date)
+    {
+        return $this->where("notifications.created_at", "<", $date);
+    }
+
+    public function readStatus($status)
+    {
+        return !$status
+            ? $this->whereNull("notification_records.read_at")
+            : $this->whereNotNull("notification_records.read_at");
+    }
+}

+ 12 - 0
app/Models/Enums/NotificationStatus.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Models\Enums;
+
+enum NotificationStatus: string
+{
+    case DRAFT = "draft";
+
+    case RELEASE = "release";
+
+    case CANCEL = "cancel";
+}

+ 2 - 1
app/Models/Notification.php

@@ -2,12 +2,13 @@
 
 namespace App\Models;
 
+use EloquentFilter\Filterable;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 
 class Notification extends Model
 {
-    use HasFactory;
+    use HasFactory, Filterable;
 
     protected $guarded = ['id'];
 }

+ 4 - 0
routes/api.php

@@ -150,5 +150,9 @@ Route::middleware(['auth:sanctum'])->group(function () {
         Route::post("file/download-zip", [API\FileController::class, "downloadZip"])->name("file.download-zip");
         Route::patch("file/{file}/change-name", [API\FileController::class, "changeName"])->name("file.change-name");
         Route::delete("file/{file}/destroy", [API\FileController::class, "destroy"])->name("file.destroy");
+
+        Route::get("notification", [API\NotificationController::class, "index"])->name("notification.index");
+        Route::get("notification/unread", [API\NotificationController::class, "unread"])->name("notification.unread");
+        Route::post("notification/mark-as-read", [API\NotificationController::class, "markAsRead"])->name("notification.mark-as-read");
     });
 });