NotificationController.php 8.36 KB
<?php

namespace App\Http\Container\AdminSection\Controllers\System;

use App\Enums\NotificationStatusEnum;
use App\Helpers\OperationLog;
use App\Http\Container\AdminSection\Requests\System\NotificationRequest;
use App\Jobs\SystemNotificationRollBackJob;
use App\Jobs\SystemNotificationSendJob;
use App\Models\Activity;
use App\Models\Notification;
use App\Models\NotificationUser;
use App\Models\Project;
use App\Models\User;
use App\Support\Controller;
use Auth;
use DB;
use Hikoon\LaravelApi\Facades\Response;
use Hikoon\LaravelApi\Support\ApiCode;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\Request;
use Str;

class NotificationController extends Controller
{

    /**
     * @param \Illuminate\Http\Request $request
     * @return \Hikoon\LaravelApi\Facades\Response
     */
    public function index(Request $request): Response
    {
        $pageSize = $request->get('pageSize', 20);
        $filter   = $request->except(['page', 'pageSize']);
        $data     = Notification::filter($filter)->withCount([
            'items',
            'items as read_count' => fn($query) => $query->whereNotNull('read_at')
        ])->paginate($pageSize);

        return $this->successWithData($data);
    }

    /**
     * @param \App\Http\Container\AdminSection\Requests\System\NotificationRequest $request
     * @return \Hikoon\LaravelApi\Facades\Response
     */
    public function store(NotificationRequest $request): Response
    {
        $attribute = $request->validated();

        $notify = DB::transaction(static function () use ($attribute) {
            $notify = Notification::query()->create($attribute + ['user_id' => Auth::id()]);

            User::filter(['status' => [0, 1], 'identityRole' => $attribute['publish_to']])->select(['id'])
                ->chunkById(1000, function (Collection $collection) use ($notify) {
                    NotificationUser::query()->insert($collection->map(fn($item) => [
                        'user_id'         => $item->getKey(),
                        'notification_id' => $notify->getKey(),
                        'created_at'      => $notify->getAttribute('created_at'),
                        'updated_at'      => $notify->getAttribute('updated_at'),
                        'status'          => NotificationStatusEnum::WAITING
                    ])->toArray());
                });

            OperationLog::admin()->subject($notify)->createAction()->content('系统通知《%s》', Str::limit($notify->getAttribute('title')));

            return $notify;
        });

        if ($notify->getAttribute('publish_type') === 1) {
            $this->send($notify);
        }

        return $this->successWithData($notify);
    }

    /**
     * @param \App\Models\Notification $notification
     * @return \Hikoon\LaravelApi\Facades\Response
     */
    public function show(Notification $notification): Response
    {
        $notification->loadCount('items');
        $linkType = $notification->getAttribute('link_type');
        $linkId   = $notification->getAttribute('link_id');

        $notification->setAttribute('link_name', match ($linkType) {
            'user' => User::query()->whereKey($linkId)->value('nick_name'),
            'project' => Project::query()->whereKey($linkId)->value('name'),
            'activity' => Activity::query()->whereKey($linkId)->value('song_name'),
            default => ''
        });

        return $this->successWithData($notification);
    }

    /**
     * @param \App\Http\Container\AdminSection\Requests\System\NotificationRequest $request
     * @param \App\Models\Notification                                             $notification
     * @return \Hikoon\LaravelApi\Facades\Response
     */
    public function update(NotificationRequest $request, Notification $notification): Response
    {
        if (!in_array($notification->getAttribute('status'), [NotificationStatusEnum::CANCEL->value, NotificationStatusEnum::ROLLBACK->value], false)) {
            return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法修改');
        }

        $attribute = $request->validated();

        $data = DB::transaction(static function () use ($notification, $attribute) {
            $notification->update($attribute + ['status' => NotificationStatusEnum::WAITING]);

            NotificationUser::query()->where('notification_id', $notification->getKey())->delete();
            User::filter(['status' => [0, 1], 'identityRole' => $attribute['publish_to']])->select(['id'])->chunkById(1000, function (Collection $collection) use ($notification) {
                NotificationUser::query()->insert($collection->map(fn($item) => [
                    'user_id'         => $item->getKey(),
                    'notification_id' => $notification->getKey(),
                    'created_at'      => $notification->getAttribute('created_at'),
                    'updated_at'      => $notification->getAttribute('updated_at')
                ])->toArray());
            });

            OperationLog::admin()->subject($notification)->updateAction()
                ->content('系统通知《%s》%s', Str::limit($notification->getAttribute('title')), $notification->getChangeColumn()->except(['status'])->format(',修改了:'));
            return $notification;
        });

        return $this->successWithData($data);
    }

    /**
     * @param \App\Models\Notification $notification
     * @return \Hikoon\LaravelApi\Facades\Response
     */
    public function send(Notification $notification): Response
    {
        if (!in_array($notification->getAttribute('status'), [NotificationStatusEnum::WAITING->value, NotificationStatusEnum::FAIL->value], false)) {
            return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法发送');
        }

        DB::transaction(static function () use ($notification) {
            $notification->update(['status' => NotificationStatusEnum::PROCESSING, 'publish_at' => now()->toDateTimeString()]);
            SystemNotificationSendJob::dispatch($notification);
        });

        return $this->successWithData(ApiCode::UPDATE_SUCCESS);
    }

    /**
     * @param \App\Models\Notification $notification
     * @return \Hikoon\LaravelApi\Facades\Response
     */
    public function cancel(Notification $notification): Response
    {
        if ($notification->getAttribute('status') !== NotificationStatusEnum::WAITING->value) {
            return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法取消发送');
        }

        $notification->update(['status' => NotificationStatusEnum::CANCEL]);

        OperationLog::admin()->subject($notification)->updateAction()->content('系统通知《%s》取消发送', Str::limit($notification->getAttribute('title')));
        return $this->success(ApiCode::UPDATE_SUCCESS);
    }

    /**
     * @param \App\Models\Notification $notification
     * @return \Hikoon\LaravelApi\Facades\Response
     * @throws \Throwable
     */
    public function rollback(Notification $notification): Response
    {
        if ($notification->getAttribute('status') !== NotificationStatusEnum::SUCCESS->value) {
            return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法撤销发送');
        }

        DB::transaction(static function () use ($notification) {
            $notification->update(['status' => NotificationStatusEnum::ROLLBACK]);
            SystemNotificationRollBackJob::dispatch($notification);
        });

        return $this->success(ApiCode::UPDATE_SUCCESS);
    }

    /**
     * @param \Illuminate\Http\Request $request
     * @param \App\Models\Notification $notification
     * @return \Hikoon\LaravelApi\Facades\Response
     */
    public function user(Request $request, Notification $notification): Response
    {
        $pageSize     = $request->get('pageSize', 20);
        $userFilter   = $request->only(['nick_name', 'authIds', 'setWith']);
        $notifyFilter = $request->only(['status', 'read_status']);
        $data         = User::filter($userFilter)->withTrashed($userFilter)->withCasts(['status' => 'int'])
            ->joinSub(NotificationUser::filter($notifyFilter)->where('notification_id', $notification->getKey()), 'notify', 'notify.user_id', 'users.id')
            ->selectRaw('users.id,users.nick_name,users.sex,users.avatar,users.official_status,users.identity,notify.status,notify.send_at,notify.read_at')
            ->orderByRaw('users.created_at')
            ->paginate($pageSize);

        return $this->successWithData($data);
    }
}