ChatRecordService.php 12.5 KB
<?php
/**
+-----------------------------------------------------------------------------------------------------------------------
 * 逻辑层:ChatRecord 服务类,处理业务逻辑
+-----------------------------------------------------------------------------------------------------------------------
 *
 * PHP version 7
 *
 * @category  App\Services
 * @package   App\Services
 * @author    Richer <yangzi1028@163.com>
 * @date      2023年4月23日16:38:09
 * @copyright 2021-2022 Richer (http://www.Richer.com/)
 * @license   http://www.Richer.com/ License
 * @link      http://www.Richer.com/
 */
namespace App\Services;

use App\Models\Category\Category;
use App\Models\Category\CategoryLabel;
use App\Models\Chat\ChatRecordItem;
use App\Models\Chat\ChatRecord;
use App\Models\System\SystemSetting;
use App\Models\Traits\ChatTrait;
use App\Models\Traits\SystemSettingTrait;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

/**
 * Class ChatRecordService
 * @package App\Services
 */
class ChatRecordService extends BaseService
{
    use ChatTrait, SystemSettingTrait;
    /**
     * ChatRecordService constructor.
     *
     * @param ChatRecord $model
     */
    public function __construct(ChatRecord $model)
    {
         // 执行父类构造方法
        parent::__construct($model);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param Request $request
     * @param bool $stream
     * @return bool|Response
     */
    public function chat($request, $stream = true)
    {
        record_log('chat', '聊天', 'begin');

        record_log('chat', '请求:', my_json_encode($request->all()));

        // 获取用户信息
        $user = $this->getLoginUser();

        // 聊天方式
        $type = $request->type;
        // 根据标签获取标签内容
        $category_id = $request->category_id;
        // 默认带上下文
        $context = 1;
        $labels = [];
        $start_statement = '';
        $consume_times = 1;
        if ($type == 2) {
            // 获取分类
            $category = Category::findOrFail($category_id);
            $start_statement = $category->start_statement;
            // 上下文
            $context = $category->context;
            // 每次消耗次数
            $consume_times = $category->consume_times;
            // 标签
            $labels_r = $request->labels;
            // 标签ID
            $label_ids = data_get($labels_r, '*.id');
            $label_item_ids = '';
            $supplement = '';
            if ($labels_r && is_array($labels_r)) {
                foreach ($labels_r as $key => $label) {
                    $label_item_ids .= ',' . implode(',', Arr::get($label, 'items.ids'));
//                $label_item_ids[] = Arr::get($label, 'items.ids');
                    if ($key === 0) {
                        $supplement = Arr::get($label, 'supplement');
                    }
                }
            }

            $label_item_ids = array_filter(array_unique(explode(',', ltrim($label_item_ids, ','))));

//        $start_statement = optional($this->getSystemSetting())->start_statement;
            $str = '';
            if ($label_ids && is_array($label_ids)) {
                $labels = CategoryLabel::with(['items' => function ($query) use ($label_item_ids) {
                    if ($label_item_ids && is_array($label_item_ids)) {
                        $query->whereIn('id', $label_item_ids);
                    }
                }])->whereIn('id', $label_ids)->get()->map(function ($label, $index) use ($supplement, &$start_statement) {
//                    $start_statement .= $label->name . ":";
                    if ($index > 0) {
                        $start_statement .= ',' . $label->name . ":";
                    } else {
                        $start_statement .=  $label->name . ":";
                    }
                    $array = [
                        'id' => $label->id,
                        'name' => $label->name,
                        'items' => $label->items->map(function ($item, $index1) use (&$start_statement) {
                            if ($index1 > 0) {
                                $start_statement .= ',' . $item->name;
                            } else {
                                $start_statement .= $item->name;
                            }

                            return [
                                'id' => $item->id,
                                'name' => $item->name,
                            ];
                        })->toArray()
                    ];
                    if ($index === 0 && $supplement) {
                        $start_statement .= ',' . $supplement;
                        $array['supplement'] = $supplement;
                    }

//                    $start_statement .= ",";
                    return $array;
                })->toArray();
            }


        }

//        if ($user->id == 1) {
//            dd($start_statement);
//        }

        $body = $request->body;
        $body_all = $body;
        if ($start_statement) {
            $body_all = $start_statement . ',' .$body;
        }

        if ($request->test === 'test') {
            dump($start_statement);
            dd($body_all);
//            $body = "";
        }

        $record = [
            'category_id'=> $category_id ?? 0,
            'type'      => $type,
            'labels'    => $labels,
            'start_statement' => $start_statement,
            'question'      => $body_all,
            'context'       => $context,
            'consume_times' => $consume_times
        ];

//        dd($record);

        DB::beginTransaction();
        try {
            // 创建本次的聊天记录
            $model = $user->chatRecords()->create($record);
            // 创建用户的聊天记录
            $item = [
                'user_id'       => $user->id,
                'category_id'   => $category_id,
                'chat_type'     => $type,
                'user_type'     => ChatRecordItem::USER,
                'body'          => $body,
                'body_all'      => $body_all,
                'ai_model'  => $this->ai_model
            ];

            $result = $model->items()->create($item);
            if (!$result) {
                record_log('chat', '聊天', 'end');
                DB::rollBack();
            }

            // TODO 根据标签组合完整的聊天内容
            // 组合方式:分类起始语句+标签名称:标签明细,标签明细,标签名称:标签明细,提问内容
            // 前置内容(每个对话场景的前置内容只有一个),标签内容(标签名称+用户选择的标签,每个标签以逗号分隔),用户输入内容
            // 发送消息
            $result = $this->send($user, $model, $body_all, $context, $stream);
            if (!$result) {
                record_log('chat', '聊天', 'end');
                return false;
            }

            // 发送消息
//            SendMessage::dispatch($model, $body, $this->ai_model);

            DB::commit();
            record_log('chat', '聊天', 'end');

            return $result;
        } catch (\Exception $e) {
            $this->message = $e->getMessage();

            DB::rollBack();
            record_log('chat', '聊天', 'end');
            return false;
        }
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param Request $request
     * @param $id
     * @param bool $stream
     * @return false|mixed
     */
    public function coutinueChat($request, $id, $stream = true)
    {
        $model = $this->model->findOrFail($id);
        $body = $request->body;
        $user = $this->getLoginUser();
        // 创建用户的聊天记录
        $item = [
            'user_id'   => $user->id,
            'user_type' => ChatRecordItem::USER,
            'category_id'   => $model->category_id,
            'chat_type'     => $model->type,
            'body'          => $body,
            'body_all'      => $body,
            'ai_model'  => $this->ai_model
        ];

        DB::beginTransaction();
        try {
            $result = $model->items()->create($item);
            if (!$result) {
                $this->message = '聊天失败。';
                return false;
            }

            // 发送消息
            $result = $this->send($user, $model, $body, $model->context, $stream);
            DB::commit();

            return $result;
        } catch (\Exception $e) {
            $this->message = $e->getMessage();

            DB::rollBack();
            return false;
        }
    }

    /**
     * Display a listing of the resource.
     *
     * @param $id
     * @return array|LengthAwarePaginator|Collection
     */
    public function items($id)
    {
        $model = $this->model->findOrFail($id);
        $list = $model->items()->oldest()->get(['id','user_id','record_id','user_type','body','ai_model','created_at'])->map(function ($item, $index) use ($model) {
            $body = (string)$item->body;
            if ($index === 0) {
                $body =  $model-> start_statement. $body;
            }

            return [
                'id'        => (int) $item->id,
                'record_id' => (int) $item->record_id,

                /* place your other model properties here */
                'body'   => $body,
                'user_type'          => (int)$item->user_type,
                'ai_model'   => (string)$item->ai_model,
                'user_type_show'   => (string)$item->user_type_show,
                'created_at'    => format_date($item->created_at),

            ];
        });
        return $list;
    }

    /**
     * Display a listing of the resource.
     *
     * @return array|LengthAwarePaginator|Collection
     */
    public function list()
    {
        return $this->model->when($q = request('name'), function ($query) use ($q) {
            $query->where("name", 'like', "%$q%");
        })->latest()->get();
    }

    /**
     * Display the specified resource.
     *
     * @param int $id 主键id
     * @param array $columns 列
     *
     * @return array|bool|Collection
     */
    public function show($id, $columns = ['*'])
    {
        $sort = request('sort', 'asc');
        $model = $this->model->with(['location','route.sites' => function ($query) use ($sort) {
            $query->with(['site'])->orderBy('sort', $sort);
//            $query->with(['route' => function ($query) use ($sort)  {
//                $query->with(['site'])->orderBy('sort', $sort);
//            }])->orderBy('sort', $sort);
        }])->findOrFail($id);
        $model->function = 'show';
        return $model;
    }

    /**
     * Display a listing of the resource.
     *
     * @return int[]
     */
    public function latestTimeout()
    {
        $user = $this->getLoginUser();
        $type = request('type', 1);
        $query = $user->chatRecords()->with(['items'])->where('type', $type);
        $category_id =  request('category_id');
        if ($type === 2) {
            $query = $query->where('category_id', $category_id);
        }

        $model = $query->latest()->first();
        // 获取缓存中的数据是否存在,如果存在
        // 获取当前会话的缓存键
        $cache_key = 'chat_context_' . $model->id;
        // 判断当前的聊天是否是基于上下文的聊天,如果是基于上下的文的聊天,需要携带上下文
        // 从缓存中获取当前会话的上下文
        $messages = Cache::get($cache_key);
        if ($messages) {
            // 将上下文放入缓存中
            $systemSetting = SystemSetting::getSetting();
            // 上下文超时时间
            $context_timeout = optional($systemSetting)->context_timeout * 60 ? : $this->context_timeout;
            Cache::put($cache_key, $messages, $context_timeout);
            return ['timeout' => false];
        }

        return ['timeout' => true];
    }

    /**
     * Display a listing of the resource.
     *
     * @return int[]
     */
    public function latest()
    {
        $user = $this->getLoginUser();
        $type = request('type', 1);
        $query = $user->chatRecords()->with(['items'])->where('type', $type);
        $category_id =  request('category_id');
        if ($type === 2) {
            $query = $query->where('category_id', $category_id);
        }

        return $query->latest()->first();
    }
}