当前位置:   article > 正文

fastadmin+xunsearch题库系统搭建教程_fastadmin答题考试系统开源

fastadmin答题考试系统开源


前言

因为前段时间需要搭建一套题库系统,但是网上并没有很好的开源题库
机缘巧合之下,看到fastadmin整合的xunsearch插件(提供了很好的搜索引擎)
由此我决定尝试使用fastadmin来搭建一套题库系统,而且fastadmin便捷的api给了搜题系统很大的接口支持。可以很简单的写出需要的接口,还不用担心鉴权的问题(接口收费、接口鉴权等各种功能都很好写,都有对应的插件,简化接口搭建时间)

本文利用fastadmin后台框架 + xunsearch插件,快速搭建题库系统

一、开始整活

服务器使用的是腾讯云的99一年的服务器
本文搭建基于CentOS8.0(xunsearch服务器端必须使用linux系统,官网也是强烈推荐)
服务器环境使用宝塔搭建


提示:xunsearch服务端可以和题库系统分开部署在不同服务器上(作者并未尝试),有需求可以自己探究,本文仅展示fastadmin框架与xunsearch插件同时部署在一台服务器上。

二、开始搭建

宝塔与fastadmin的搭建就不再介绍了,这两个搭建很简单,网上都有教程。
fastadmin关于xunsearch文档的文档(你也可以根据此文档完成配置)

1.fastadmin配置

我们要将fastadmin插件市场中xunsearch插件安装进我们的框架
在这里插入图片描述

2.xunsearch服务端安装

离线安装:讯搜官网
在线安装(Linux系统下):

curl -O http://www.xunsearch.com/download/xunsearch-full-latest.tar.bz2
tar -xvf xunsearch-full-latest.tar.bz2 
  • 1
  • 2

安装 1.4.15版本

cd xunsearch-full-1.4.15
sh setup.sh
  • 1
  • 2

接下来就是等待安装完成
安装出现的问题:

(一).openssl的版本与libevent版本不对应

在这里插入图片描述
在这里插入图片描述
在centos8中升级了openssl,导致xunseach内libevent版本对应不上导致报错

解决方案

手动替换libevent文件
下载libevent文件2.1.12版本(我替换了这个版本解决了这个问题,其他版本尚未确认)

wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
  • 1

将下载的文件打包

tar -zxvf libevent-2.1.12-stable.tar.gz
  • 1

复制一份到xunsearch的packages文件夹内(删除旧版本的libevent)

cp libevent-2.1.12-stable.tar.bz2  packages/
  • 1

替换后
在这里插入图片描述
这样重新安装就不会出现版本不一致导致libevent出现问题

2.安装完成后配置

启动服务
当安装完成Xunsearch后,我们可以通过以下命令进行启动服务

cd xunsearch-full-1.4.15
cd bin
./xs-ctl.sh start
  • 1
  • 2
  • 3

启动完成后就可以和我们的fastadmin进行交互了(使用fastadmin默认配置即可)
接下来我们进入fastadmin添加一个项目
在这里插入图片描述
字段配置
必须要添加(id/title/body)这三个类型,不创建将导致配置生成失败
在这里插入图片描述
接下来我们生成一下配置
在这里插入图片描述

这时我们的题库已经搭建完成了,我们点击前台即可进入搜索页面.
在这里插入图片描述
在这里插入图片描述
但是这个时候我们的题库并没有数据,别着急请接着完成下面数据库的搭建!

3.数据库搭建

我们进入宝塔管理面板,去创建一个新的数据库
在这里插入图片描述
数据表创建与字段设置(请与上方fastadmin里xunsearch项目字段保持一致)
在这里插入图片描述
这时候请不要把题库导入该数据库

4.fastadmin生成与代码修改

(一).fastadmin数据表、控制器、菜单创建与生成

这时我们来到最后一步
用fastadmin中的在线命令管理(这个也是个插件,需要在插件市场安装后使用),生成该数据表的控制器
在这里插入图片描述
在这里插入图片描述
菜单创建
在这里插入图片描述
我们就可以看到菜单出现(如果没有多出一个菜单请刷新缓存)
在这里插入图片描述
我这个是修改过菜单名称的,初始名称并不是这样,不过这个并不重要
搭建到这个步骤可以看到使用这个菜单可以往数据库中加入数据,但是这样并不会往搜索引擎中加入索引数据

(二).fastadmin控制器修改,调用api添加索引数据库

以下内容使用到fastadmin的xunsearch文档中关于api的调用

我们打开宝塔,进入网站根目录,用默认的宝塔文件管理打开
目录为:/www/wwwroot/shouti/application/admin/controller/sou/Tiku.php
如果你一键生成的控制器不和我相同,那么最后文件目录也会有差异,这一步需要根据自己配置来找
在这里插入图片描述
打开后修改内容

默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
  • 1
  • 2
  • 3

我们可以在该文件中看到这个提示,根据提示,我们从[application/admin/library/traits/Backend.php]中找到添加、编辑、删除的代码

 /**
     * 添加
     */
    public function add()
    {
        if ($this->request->isPost()) {
            $params = $this->request->post("row/a");
            if ($params) {
                $params = $this->preExcludeFields($params);

                if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
                    $params[$this->dataLimitField] = $this->auth->id;
                }
                $result = false;
                Db::startTrans();
                try {
                    //是否采用模型验证
                    if ($this->modelValidate) {
                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
                        $this->model->validateFailException(true)->validate($validate);
                    }
                    $result = $this->model->allowField(true)->save($params);
                    Db::commit();
                } catch (ValidateException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (PDOException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (Exception $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                }
                if ($result !== false) {
                    $this->success();
                } else {
                    $this->error(__('No rows were inserted'));
                }
            }
            $this->error(__('Parameter %s can not be empty', ''));
        }
        return $this->view->fetch();
    }

    /**
     * 编辑
     */
    public function edit($ids = null)
    {
        $row = $this->model->get($ids);
        if (!$row) {
            $this->error(__('No Results were found'));
        }
        $adminIds = $this->getDataLimitAdminIds();
        if (is_array($adminIds)) {
            if (!in_array($row[$this->dataLimitField], $adminIds)) {
                $this->error(__('You have no permission'));
            }
        }
        if ($this->request->isPost()) {
            $params = $this->request->post("row/a");
            if ($params) {
                $params = $this->preExcludeFields($params);
                $result = false;
                Db::startTrans();
                try {
                    //是否采用模型验证
                    if ($this->modelValidate) {
                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
                        $row->validateFailException(true)->validate($validate);
                    }
                    $result = $row->allowField(true)->save($params);
                    Db::commit();
                } catch (ValidateException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (PDOException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (Exception $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                }
                if ($result !== false) {
                    $this->success();
                } else {
                    $this->error(__('No rows were updated'));
                }
            }
            $this->error(__('Parameter %s can not be empty', ''));
        }
        $this->view->assign("row", $row);
        return $this->view->fetch();
    }

    /**
     * 删除
     */
    public function del($ids = "")
    {
        if (!$this->request->isPost()) {
            $this->error(__("Invalid parameters"));
        }
        $ids = $ids ? $ids : $this->request->post("ids");
        if ($ids) {
            $pk = $this->model->getPk();
            $adminIds = $this->getDataLimitAdminIds();
            if (is_array($adminIds)) {
                $this->model->where($this->dataLimitField, 'in', $adminIds);
            }
            $list = $this->model->where($pk, 'in', $ids)->select();

            $count = 0;
            Db::startTrans();
            try {
                foreach ($list as $k => $v) {
                    $count += $v->delete();
                }
                Db::commit();
            } catch (PDOException $e) {
                Db::rollback();
                $this->error($e->getMessage());
            } catch (Exception $e) {
                Db::rollback();
                $this->error($e->getMessage());
            }
            if ($count) {
                $this->success();
            } else {
                $this->error(__('No rows were deleted'));
            }
        }
        $this->error(__('Parameter %s can not be empty', 'ids'));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

我们将这段代码复制进刚刚打开的文件中
在这里插入图片描述
同时将这些方法根据fastadmin提供的api进行修改,达到添加xunsearch数据库索引的效果
添加修改后代码

/**
     * 添加
     */
    public function add()
    {
        $search = \addons\xunsearch\library\Xunsearch::instance("souti");
        
        
        if ($this->request->isPost()) {
            $params = $this->request->post("row/a");
            if ($params) {
                $params = $this->preExcludeFields($params);

                if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
                    $params[$this->dataLimitField] = $this->auth->id;
                }
                $result = false;
                
                Db::startTrans();
                try {
                    //是否采用模型验证
                    if ($this->modelValidate) {
                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
                        $this->model->validateFailException(true)->validate($validate);
                    }
                    $result = $this->model->allowField(true)->save($params);
                    Db::commit();
                    $lastid = Db::table('sou_tiku')->where('id>0')->max('id');
                    $data = [
                        'id'=>$lastid,
                        'title'=>"[ID".$lastid."]:".$params['title'],
                        'body'=>$params['body'],
                        'ans'=> $params['ans']
                    ];
                    $search->add($data);
                } catch (ValidateException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (PDOException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (Exception $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                }
                if ($result !== false) {
                    $this->success();
                } else {
                    $this->error(__('No rows were inserted'));
                }
            }
            $this->error(__('Parameter %s can not be empty', ''));
        }
        
        return $this->view->fetch();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

添加部分解析
我首先获取到最后一位id(添加进索引数据库必须拥有一个自己的id,通过id才可以修改此条题目状态),这个id我放到题目上,如果用户投诉答案出错,可以向开发者提供此题目id,开发者可以根据id快速定位该题目
然后接下来就是根据fastadmin写好的,获取每一条数据,根据fastadmin提供的api导入进索引数据库

$lastid = Db::table('sou_tiku')->where('id>0')->max('id');
                    $data = [
                        'id'=>$lastid,
                        'title'=>"[ID".$lastid."]:".$params['title'],
                        'body'=>$params['body'],
                        'ans'=> $params['ans']
                    ];
                    $search->add($data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

编辑修改后代码

/**
     * 编辑
     */
    public function edit($ids = null)
    {
        $search = \addons\xunsearch\library\Xunsearch::instance("souti");
        $row = $this->model->get($ids);
        if (!$row) {
            $this->error(__('No Results were found'));
        }
        $adminIds = $this->getDataLimitAdminIds();
        if (is_array($adminIds)) {
            if (!in_array($row[$this->dataLimitField], $adminIds)) {
                $this->error(__('You have no permission'));
            }
        }
        if ($this->request->isPost()) {
            $params = $this->request->post("row/a");
            if ($params) {
                $params = $this->preExcludeFields($params);
                $result = false;
                Db::startTrans();
                try {
                    //是否采用模型验证
                    if ($this->modelValidate) {
                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
                        $row->validateFailException(true)->validate($validate);
                    }
                    $result = $row->allowField(true)->save($params);
                    Db::commit();
                    $data = [
                        'id'=>$ids,
                        'title'=>"[ID".$ids."]:".$params['title'],
                        'body'=>$params['body'],
                        'ans'=>$params['ans']
                    ];
                    $search->update($data);
                } catch (ValidateException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (PDOException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (Exception $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                }
                if ($result !== false) {
                    $this->success();
                } else {
                    $this->error(__('No rows were updated'));
                }
            }
            $this->error(__('Parameter %s can not be empty', ''));
        }
        $this->view->assign("row", $row);
        return $this->view->fetch();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

编辑部分解析
编辑该数据的同时,我们将此次编辑也通过api进行修改到索引数据库

					$data = [
                        'id'=>$ids,
                        'title'=>"[ID".$ids."]:".$params['title'],
                        'body'=>$params['body'],
                        'ans'=>$params['ans']
                    ];
                    $search->update($data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

删除修改后代码

/**
     * 删除
     */
    public function del($ids = "")
    {
        $search = \addons\xunsearch\library\Xunsearch::instance("souti");
        if (!$this->request->isPost()) {
            $this->error(__("Invalid parameters"));
        }
        $ids = $ids ? $ids : $this->request->post("ids");
        if ($ids) {
            $pk = $this->model->getPk();
            $adminIds = $this->getDataLimitAdminIds();
            if (is_array($adminIds)) {
                $this->model->where($this->dataLimitField, 'in', $adminIds);
            }
            $list = $this->model->where($pk, 'in', $ids)->select();

            $count = 0;
            Db::startTrans();
            try {
                foreach ($list as $k => $v) {
                    $count += $v->delete();
                }
                Db::commit();
                $search->del($ids);
            } catch (PDOException $e) {
                Db::rollback();
                $this->error($e->getMessage());
            } catch (Exception $e) {
                Db::rollback();
                $this->error($e->getMessage());
            }
            if ($count) {
                $this->success();
            } else {
                $this->error(__('No rows were deleted'));
            }
        }
        $this->error(__('Parameter %s can not be empty', 'ids'));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

删除部分解析
这里是当数据表中数据删除的时候,我们也应该删除索引数据库里面的数据

$search->del($ids);
  • 1

至此从fastadmin中添加/编辑/删除的操作,全部会同步到题库系统中.
重要提示:在fastadmin控制台只能一条一条的插入题库,因为导入的代码并未修改,所以导入进去的数据是无法查询的
在这里插入图片描述
这里id不一致是因为添加的时候id没有+1,上面代码未修改此问题,请自行修改[添加]部分代码.
在这里插入图片描述

(三).接口搭建

因为一个一个导入真的是太慢了.
我这边解决方案就是搭建一个接口,通过接口上传题目
在这里插入图片描述
我这边创建了一个新的接口,如果是小白,可以把接口搭建在demo中
在这里插入图片描述
同时将接口鉴权关闭
加入题目函数

public function insertans()
    {
        $search = \addons\xunsearch\library\Xunsearch::instance("souti");
        
        $title = $this->request->request('title');
        $body = $this->request->request('body');
        $ans = $this->request->request('ans');
        $lastid = Db::table('sou_tiku')->where('id>0')->max('id');
        $data = ['title' => $title, 'body' => $body , 'ans' => $ans];
        Db::startTrans();
        try {
                Db::table('sou_tiku')->insert($data);
                Db::commit();
                $sdata = [
                    'id'=>$lastid,
                    'title'=>"[ID".$lastid."]:".$title,
                    'body'=>$body,
                    'ans'=> $ans
                ];
                $search->add($sdata);
            } catch (ValidateException $e) {
                Db::rollback();
                $this->error($e->getMessage());
            } catch (PDOException $e) {
                Db::rollback();
                $this->error($e->getMessage());
            } catch (Exception $e) {
                Db::rollback();
                $this->error($e->getMessage());
            }
        $this->success('返回成功',$res);
        //Db::table('sou_tiku')->insert($data);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

搜索题目返回函数

public function searchans()
    {
        $que = $this->request->request('q');
        $search = \addons\xunsearch\library\Xunsearch::instance("souti");
        $res = $search->search($que,1,10);
        $this->success('返回成功',$res); 
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果你实在不会写,请在该目录下创建sou.php
并加入以下代码

<?php

namespace app\api\controller;

use app\common\controller\Api;
use think\Db;
/**
 * 示例接口
 */

class Sou extends Api
{

    //如果$noNeedLogin为空表示所有接口都需要登录才能请求
    //如果$noNeedRight为空表示所有接口都需要验证权限才能请求
    //如果接口已经设置无需登录,那也就无需鉴权了
    //
    // 无需登录的接口,*表示全部
    protected $noNeedLogin = ['searchans','insertans'];
    // 无需鉴权的接口,*表示全部
    protected $noNeedRight = [];
  
    public function insertans()
    {
        $search = \addons\xunsearch\library\Xunsearch::instance("souti");
        
        $title = $this->request->request('title');
        $body = $this->request->request('body');
        $ans = $this->request->request('ans');
        $lastid = Db::table('sou_tiku')->where('id>0')->max('id');
        $data = ['title' => $title, 'body' => $body , 'ans' => $ans];
        Db::startTrans();
        try {
                Db::table('sou_tiku')->insert($data);
                Db::commit();
                $sdata = [
                    'id'=>$lastid,
                    'title'=>"[ID".$lastid."]:".$title,
                    'body'=>$body,
                    'ans'=> $ans
                ];
                $search->add($sdata);
            } catch (ValidateException $e) {
                Db::rollback();
                $this->error($e->getMessage());
            } catch (PDOException $e) {
                Db::rollback();
                $this->error($e->getMessage());
            } catch (Exception $e) {
                Db::rollback();
                $this->error($e->getMessage());
            }
        $this->success('返回成功',$res);
        //Db::table('sou_tiku')->insert($data);
    }
    public function searchans()
    {
        $que = $this->request->request('q');
        $search = \addons\xunsearch\library\Xunsearch::instance("souti");
        $res = $search->search($que,1,10);
        $this->success('返回成功',$res); 
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

具体代码含义,请自行查询thinkphp

(四).接口测试

1.插入题目

调用网址:https://你的网站/api/sou/insertans?title=&body=&ans=
中文必须进行url编码,可以使用get或者post进行提交,title为题目,body为选项,ans为答案
具体字段可以自行修改

2.查询题目

调用网址:http://你的网站/api/sou/searchans?q="zg"
返回的是一段json代码,可以根据需求自己解析使用

总结

搭建比较繁琐,新手建议先看fastadmin官方给的教程,bilibili上也有的
小白请先看上面教程,不然会安装到你崩溃
看完前几集其实就差不多了,可以对比此教程看视频,学会控制器修改,api接口即可搭建
在thinkphp方面作者也是个小白,教程中一些地方写的并不是很标准,目的只是搭建能够查询的题库,更安全更标准的搭建方式,请自行探索

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/449259
推荐阅读
相关标签
  

闽ICP备14008679号