IMCallbackController.php 10.1 KB
<?php

namespace App\Http\Container\ProviderSection\Controllers;

use App\Exceptions\TestCallbackException;
use App\Helpers\IMHelper;
use App\Jobs\ImMessageSaveJob;
use App\Jobs\WechatTemplateMessageJob;
use App\Models\ActivityGroup;
use App\Models\ActivityGroupUser;
use App\Models\SystemConfig;
use App\Models\User;
use Carbon\Carbon;
use GuzzleHttp\Promise\PromiseInterface;
use Hikoon\LaravelApi\Support\ApiController;
use Illuminate\Http\Client\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;

class IMCallbackController extends ApiController
{
    /**
     * @param string $key
     * @return bool
     */
    protected function gate(string $key): bool
    {
        return Str::of($key)->match('/^' . config('services.im.prefix') . '\d+$/')->isNotEmpty();
    }

    /**
     * @param \Illuminate\Http\Request $request
     * @return \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response|\Illuminate\Http\JsonResponse
     * @throws \Laravel\Octane\Exceptions\DdException
     */
    public function __invoke(Request $request): PromiseInterface|JsonResponse|Response
    {
        $method = Str::of($request->input('CallbackCommand'))->replace('.', '')->lcfirst()->toString();

        if (method_exists($this, $method)) {
            try {
                return $this->$method($request->except(['CallbackCommand']));
            } catch (TestCallbackException) {
                return App::isProduction() ?
                    Http::asJson()->timeout(2)->post('https://hi-sing-service-dev.hikoon.com/provider/imCallback', $request->all()) :
                    new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
            }
        }

        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }

    /**
     * @param array $data
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    protected function stateStateChange(array $data): JsonResponse
    {
        $platform = Arr::get($data, 'OptPlatform');
        $userId   = Arr::get($data, 'Info.To_Account');
        $action   = Arr::get($data, 'Info.Action');

        throw_unless($this->gate($userId), TestCallbackException::class);


        if ($action === 'Login') {
            User::query()->whereKey(IMHelper::accountToUserKey($userId))->update(['im_client' => $platform, 'im_status' => 1]);
            Redis::client()->hDel('imOfflineMsg', IMHelper::accountToUserKey($userId));
        } else {
            User::query()->whereKey(IMHelper::accountToUserKey($userId))->where('im_client', $platform)->update(['im_status' => 0]);
        }

        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }

    /**
     * @throws \Throwable
     */
    protected function c2CCallbackAfterSendMsg(array $data): JsonResponse
    {
        $toId   = Arr::get($data, 'To_Account');
        $formId = Arr::get($data, 'From_Account');
        $online = Arr::get($data, 'OnlineOnlyFlag', 0);
        $time   = Arr::get($data, 'MsgTime', Carbon::now()->getTimestamp());

        throw_unless($this->gate($formId), TestCallbackException::class);

        $hashKey  = 'imOfflineMsg';
        $sendTime = Carbon::createFromTimestamp($time)->toDateTimeString();
        $toId     = IMHelper::accountToUserKey($toId);
        $formId   = IMHelper::accountToUserKey($formId);


        ImMessageSaveJob::dispatch($data);

        $openId = User::query()
            ->where('id', $toId)
            ->where('audit_status', 1)
            ->where('status', 1)
            ->where('im_client', 'Web')
            ->where('im_status', 0)
            ->value('official_id');

        if ($openId && (int)$online === 0 && !Redis::client()->hExists($hashKey, $formId)) {
            Redis::client()->hSet($hashKey, $formId, 0);
            $customerId = SystemConfig::query()->where('identifier', 'customer_service')->value('remark');
            $formUser   = User::with('artTags:id,name')->find($formId, ['id', 'nick_name']);
            $formName   = $formUser->getAttribute('nick_name');
            $formTag    = $formUser->getRelation('artTags') ? $formUser->getRelation('artTags')->implode('name', ',') : '无';

            $data = $customerId === $formId ?
                ['first' => '您收到平台客服发来的消息', 'keyword1' => $formName, 'keyword2' => '平台客服', 'keyword3' => '平台官方', 'keyword4' => $sendTime, 'remark' => '点击查看'] :
                ['first' => '您收到新的私聊消息', 'keyword1' => $formName, 'keyword2' => $formTag, 'keyword3' => '私聊消息', 'keyword4' => $sendTime, 'remark' => '点击查看'];


            WechatTemplateMessageJob::dispatch($openId, 'HTRAwaRoJksSGCiSeVCyvpF9vXdeQhC6R4PbSYnO8NM', $data);
        }

        Redis::client()->hIncrBy($hashKey, $formId, 1);

        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }

    /**
     * @param array $data
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    protected function groupCallbackAfterCreateGroup(array $data): JsonResponse
    {
        $groupId = Arr::get($data, 'GroupId');

        throw_unless($this->gate($groupId), TestCallbackException::class);

        $ownerId             = IMHelper::accountToUserKey(Arr::get($data, 'Owner_Account', 0));
        $userDefinedDataList = Arr::pluck($data['UserDefinedDataList'], 'Value', 'Key');

        $group      = ActivityGroup::query()->firstOrCreate(['im_group_id' => $groupId], ['user_id' => $ownerId, 'activity_id' => Arr::get($userDefinedDataList, 'ActivityKey', 0), 'name' => $data['Name']]);
        $memberList = Arr::map($data['MemberList'], static fn($item) => ['user_id' => IMHelper::accountToUserKey($item['Member_Account']), 'group_id' => $group->getKey()]);

        ActivityGroupUser::query()->upsert($memberList, ['user_id', 'guard_id']);

        IMHelper::sendGroupMessage($groupId, [
            "MsgBody" => [
                IMHelper::createCustomMessage([
                    "Data" => [
                        "businessID" => "group_inviter_user",
                        'inviter'    => User::query()->whereKey($ownerId)->value('nick_name'),
                        'memberList' => User::query()->whereIn('id', array_column($memberList, 'user_id'))->pluck('nick_name')->toArray(),
                        "Desc"       => ''
                    ]
                ])
            ]
        ]);

        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }

    /**
     * @param array $data
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    protected function groupCallbackAfterGroupInfoChanged(array $data): JsonResponse
    {
        $groupId = Arr::get($data, 'GroupId');

        throw_unless($this->gate($groupId), TestCallbackException::class);

        if (isset($data['Name'])) {
            ActivityGroup::query()->where('im_group_id', $groupId)->update(['name' => $data['Name']]);
        }

        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }

    /**
     * @param array $data
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    protected function groupCallbackAfterNewMemberJoin(array $data): JsonResponse
    {
        $groupId = Arr::get($data, 'GroupId');

        throw_unless($this->gate($groupId), TestCallbackException::class);

        if ($group = ActivityGroup::query()->where('im_group_id', $groupId)->value('id')) {
            $inviterUser = IMHelper::accountToUserKey(Arr::get($data, 'Operator_Account', 0));
            $memberList  = Arr::map($data['NewMemberList'], static fn($item) => ['user_id' => IMHelper::accountToUserKey($item['Member_Account']), 'group_id' => $group]);
            ActivityGroupUser::query()->upsert($memberList, ['user_id', 'group_id']);

            IMHelper::sendGroupMessage($groupId, [
                "MsgBody" => [
                    IMHelper::createCustomMessage([
                        "Data" => [
                            "businessID" => "group_inviter_user",
                            'inviter'    => User::query()->whereKey($inviterUser)->value('nick_name'),
                            'memberList' => User::query()->whereIn('id', array_column($memberList, 'user_id'))->pluck('nick_name')->toArray(),
                            "Desc"       => ''
                        ]
                    ])
                ]
            ]);
        }
        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }

    /**
     * @param array $data
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    protected function groupCallbackAfterMemberExit(array $data): JsonResponse
    {
        $groupId = Arr::get($data, 'GroupId');

        throw_unless($this->gate($groupId), TestCallbackException::class);

        if ($group = ActivityGroup::query()->where('im_group_id', $groupId)->value('id')) {
            $memberList = Arr::map($data['ExitMemberList'], static fn($item) => IMHelper::accountToUserKey($item['Member_Account']));
            ActivityGroupUser::query()->where('group_id', $group)->whereIn('user_id', $memberList)->delete();
        }

        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }

    /**
     * @param array $data
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    protected function groupCallbackAfterGroupDestroyed(array $data): JsonResponse
    {
        $groupId = Arr::get($data, 'GroupId');

        throw_unless($this->gate($groupId), TestCallbackException::class);

        if ($group = ActivityGroup::query()->where('im_group_id', $groupId)->value('id')) {
            ActivityGroup::query()->whereKey($group)->delete();
            ActivityGroupUser::query()->where('group_id', $group)->delete();
        }

        return new JsonResponse(['ActionStatus' => 'OK', 'ErrorInfo' => '', 'ErrorCode' => 0]);
    }
}