<?php

namespace App\Services\Book;


use App\Cache\StatisticCache;
use App\Cache\UserCache;
use App\Consts\BaseConst;
use App\Consts\CoinConst;
use App\Consts\ErrorConst;
use App\Facade\Site;
use App\Libs\Utils;
use App\Models\Book\Book;
use App\Models\Book\BookConfig;
use App\Models\Book\Category;
use App\Models\Book\Chapter;
use App\Models\User\User;
use App\Services\OpenApi\OpenService;
use App\Services\Subscribe\ChapterOrderService;
use App\Services\User\UserService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Vinkla\Hashids\Facades\Hashids;

class BookService
{
    protected $userService;
    protected $openService;
    protected $chapterOrderService;

    public function __construct(UserService $userService, OpenService $openService, ChapterOrderService $chapterOrderService)
    {
        $this->userService         = $userService;
        $this->openService         = $openService;
        $this->chapterOrderService = $chapterOrderService;
    }

    /**
     * 书籍列表
     *
     * @param $data
     * @return mixed
     */
    public function getBookList($data)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $gender      = getProp($data, 'gender', 2);               // 男女频
        $book_name   = trim(getProp($data, 'book_name'));         // 书名
        $charge_type = getProp($data, 'charge_type');             // 篇幅(长篇:CHAPTER 短篇:BOOK)
        $status      = getProp($data, 'status');                  // 连载状态(0.连载中 1.已完结)
        $roles       = getProp($data, 'roles');                   // 主角名称
        $page_number = getProp($data, 'page_number');

        $query = Book::leftJoin('book_configs', 'books.id', 'book_configs.bid')->whereIn('book_configs.is_on_shelf', [1, 2])
                     ->leftJoin('book_categories', 'book_categories.id', 'books.ncategory_id')
                     ->leftJoin('channel_book_charge_type', function ($join) use ($distribution_channel_id) {
                         $join->on('channel_book_charge_type.bid', '=', 'books.id')->where('channel_book_charge_type.distribution_channel_id', '=', $distribution_channel_id);
                     })
                     ->leftJoin('books as b1', 'b1.id', 'book_configs.origin_bid')
                     ->where('books.gender', $gender)->select('books.id', 'books.name', 'books.intro', 'books.cover',
                'book_configs.origin_bid', 'books.author', 'books.category_name', 'books.gender', 'books.status',
                'book_categories.category_name as ncategory_name', 'books.chapter_count', 'books.size', 'book_configs.book_name',
                'book_configs.roles', 'book_configs.charge_type', 'books.created_at', 'b1.name as origin_book_name',
                'channel_book_charge_type.book_calculate_price_type', 'channel_book_charge_type.book_coin');

        if ($book_name) {
            $bid = BookConfig::where('book_name', $book_name)->value('bid');
            if ($bid) {     // 如果精准搜索到书籍
                $query->where('books.id', $bid)->orWhere('book_configs.origin_bid', $bid)->orderBy('book_configs.origin_bid');
            } else {
                $query->where('book_configs.book_name', 'like', '%' . $book_name . '%');
            }
        } else {
            $query->where('book_configs.origin_bid', 0);
        }

        if ($charge_type) {
            $query->where('book_configs.charge_type', $charge_type);
        }

        if ($status !== '') {
            $query->where('books.status', $status);
        }

        if ($roles) {
            $query->where('book_configs.roles', 'like', '%' . $roles . '%');
        }

        if ($page_number) {
            return $query->orderBy('books.created_at', 'desc')->paginate($page_number);
        } else {
            return $query->orderBy('books.created_at', 'desc')->paginate();
        }
    }

    public function simpleBookConfigs($bids)
    {
        return BookConfig::select('bid', 'book_name')->whereIn('bid', $bids)->get();
    }

    /**
     * 热门搜索
     *
     * @return array
     */
    public function getHotSearches()
    {
        $result = DB::table('hot_search_words')->get()->pluck('hot_word')->toArray();
        $result = array_unique($result);
        shuffle($result);
        return $result;
    }

    /**
     * 书籍详情
     *
     * @param $data
     * @return array
     */
    public function getBookDetail($data)
    {
        Log::info('书籍详情页入参: ' . json_encode($data, 256));
        $uid = Site::getUid();

        $user_info = User::find($uid);
        $bid       = getProp($data, 'bid');
        if (mb_strlen($bid) === 32) {
            $bid = Hashids::decode($bid)[0];
        }
        if (!$bid) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        $result = Book::leftJoin('book_configs', 'book_configs.bid', 'books.id')
                      ->where('books.id', $bid)
                      ->whereIn('book_configs.is_on_shelf', [1, 2])
                      ->select('books.id as bid', 'books.name as book_name', 'books.category_name', 'books.category_id',
                          'books.size', 'books.status', 'books.cover', 'books.intro')->first();
        if (!$result) return [];

        $book['detail'] = $result->toArray();
        $book['user']   = [
            'balance'        => getProp($user_info, 'balance', 0),
            'charge_balance' => getProp($user_info, 'charge_balance', 0),
            'reward_balance' => getProp($user_info, 'reward_balance', 0),
        ];

        // 获取本书前两章内容
        $front_double_chapters                 = Chapter::leftJoin('chapter_contents', 'chapter_contents.id', 'chapters.chapter_content_id')
                                                        ->select('chapters.id as cid', 'chapters.name as chapter_name', 'chapter_contents.content as chapter_content')
                                                        ->where('chapters.bid', $bid)->where(['chapters.is_check' => 1, 'chapters.is_deleted' => 0])->orderBy('sequence')
                                                        ->limit(2)->get()->toArray();
        $book['first_chapter']                 = $book['second_chapter'] = [];
        $book['front_double_chapter_contents'] = '';
        $i                                     = 1;
        foreach ($front_double_chapters as &$item) {
            $book['front_double_chapter_contents'] .= $item['chapter_name'] . PHP_EOL . filterContent($item['chapter_content']) . PHP_EOL . PHP_EOL;
            $item['cid']                           = Hashids::encode($item['cid']);
            $item['chapter_content']               = filterContent($item['chapter_content']);
            if ($i === 1) $book['first_chapter'] = $item;
            if ($i === 2) $book['second_chapter'] = $item;
            $i++;
        }

        // 获取最新章节
        $book['latest_chapter'] = Chapter::where(['bid' => $bid, 'is_check' => 1, 'is_deleted' => 0])->orderBy('sequence', 'desc')
                                         ->select('id as cid', 'name as chapter_name')->first();
        if ($book['latest_chapter']) {
            $book['latest_chapter']           = $book['latest_chapter']->toArray();
            $book['latest_chapter']['cid']    = Hashids::encode($book['latest_chapter']['cid']);
            $book['latest_chapter']['status'] = $book['detail']['status'];
        } else {
            $book['latest_chapter'] = [];
        }

        // 格式化书籍数据
        $book['detail']['cover']       = addPrefix($book['detail']['cover']);
        $book['detail']['size']        = round($result['size'] / 10000, 1) . '万';
        $book['detail']['status_info'] = (int)$result['status'] === 1 ? '已完结' : '连载中';
        $book['detail']['bid']         = Hashids::encode($bid);
        if (!$book['detail']['category_name']) $book['detail']['category_name'] = '其他';

        // 获取充值排名前20的书籍(每次都随机排序)
        $charge_books            = $this->getBookOrderByCharge($book['detail']['category_id']);
        $book['recommend_books'] = [];
        $max                     = DB::table('books')->count('id');
        if ($max < 20) $max -= 1;
        else $max = 19;
        $arr_index = range(0, $max);
        shuffle($arr_index);
        $arr_index = array_slice($arr_index, 0, 6);

        foreach ($arr_index as $index) {
            if (isset($charge_books[$index])) {
                $arr                       = $charge_books[$index];
                $book['recommend_books'][] = [
                    'bid'       => Hashids::encode($arr['id']),
                    'cover'     => addPrefix($arr['cover']),
                    'book_name' => $arr['name'],
                ];
            }
        }

        $book['promotion_code'] = '';
        // 生成视频推广口令码
        if ($uid) {
            $book['promotion_code'] = Utils::getPasswordCode($uid, $bid);
        }

        // 获取分享链接中的隐藏参数(如果存在隐藏参数邀请码则绑定)
        $invite_code = getProp($data, 'invite_code');
        $from_uid    = User::where('invite_code', $invite_code)->value('id');
        if ($from_uid) {
            $info = $this->userService->bind($from_uid, $uid);  // 绑定邀请码
            Log::info('书籍详情页邀请码绑定结果-----from_uid: ' . $from_uid . ';uid: ' . $uid . ';info: ' . $info);
        }

        // 获取分享链接中的隐藏参数(如果存在隐藏参数派单id则绑定)
        $send_order_id = getProp($data, 'send_order_id');
        if ($send_order_id && $uid) {
            $info = $this->userService->bindSendOrder($send_order_id);  // 绑定派单链接
            Log::info('书籍详情页派单链接绑定结果-----send_order_id: ' . $send_order_id . ';uid: ' . $uid . ';info: ' . $info);
        }

        return $book;
    }

    /**
     * 获取某本书的阅读记录
     *
     * @param $data
     * @return array|false|mixed
     * @throws \App\Exceptions\ApiException
     */
    public function recentChapter($data)
    {
        $uid = Site::getUid();

        $bid = getProp($data, 'bid');
        if (mb_strlen($bid) === 32) {
            $bid = Hashids::decode($bid)[0];
        }
        if (!$bid) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        $recent_book = UserCache::getRecentBooksByBid($uid, $bid);
        if ($recent_book) {
            $recent_book['bid'] = Hashids::encode($recent_book['bid']);
            $recent_book['cid'] = Hashids::encode($recent_book['cid']);
        } else {
            $chapter = Chapter::leftJoin('books', 'books.id', 'chapters.bid')
                              ->leftJoin('book_configs', 'chapters.bid', 'book_configs.bid')->whereIn('book_configs.is_on_shelf', [1, 2])
                              ->where(['chapters.bid' => $bid, 'chapters.is_check' => 1, 'chapters.is_deleted' => 0])
                              ->orderBy('chapters.sequence')->select('chapters.bid', 'books.name as book_name', 'books.cover', 'chapters.id as cid',
                    'chapters.name as chapter_name', 'chapters.sequence', 'books.gender')->first();
            if ($chapter) {
                $chapter              = $chapter->toArray();
                $chapter['bid']       = Hashids::encode($chapter['bid']);
                $chapter['cid']       = Hashids::encode($chapter['cid']);
                $chapter['read_time'] = '';
                $recent_book          = $chapter;
            } else {
                $recent_book = new \stdClass();
            }
        }
        return $recent_book;
    }

    /**
     * 生成分享链接参数(小程序内部调用)
     *
     * @param $data
     * @return array|string
     * @throws \App\Exceptions\ApiException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function setUrlLink($data)
    {
        $type                    = getProp($data, 'type');
        $send_order_id           = getProp($data, 'send_order_id');
        $distribution_channel_id = getProp($data, 'distribution_channel_id');
        $template_id             = getProp($data, 'template_id', env('SHARE_TEMPLATE_ID'));
        $uid                     = Site::getUid();
        $invite_code             = User::where('id', $uid)->value('invite_code');
        if (!$invite_code) $invite_code = '';
        $data['invite_code'] = $invite_code;
        Log::info('小程序内部调用分享链接参数: ' . json_encode($data, 256));

        $bid = getProp($data, 'bid');
        $cid = getProp($data, 'cid');
        if (mb_strlen($bid) === 32) {
            $bid = Hashids::decode($bid)[0];
        }
        if (mb_strlen($cid) === 32) {
            $cid = Hashids::decode($cid)[0];
        }

        // 如果有派单链接,则替换bid和cid
        if ($send_order_id) {
            $send_order = DB::table('send_orders')->where('id', $send_order_id)->select('distribution_channel_id', 'book_id', 'chapter_id')->first();
            if ($send_order) {
                $bid                     = getProp($send_order, 'book_id');
                $cid                     = getProp($send_order, 'chapter_id');
                $distribution_channel_id = getProp($send_order, 'distribution_channel_id');
            }
        }

        $promotion_code = '';
        if ($bid) $promotion_code = DB::table('user_book_code')->where(['bid' => $bid, 'uid' => $uid])->value('id');

        $arr = [
            'path'                    => 'pages/index/index',
            'bid'                     => $bid ? Hashids::encode($bid) : '',
            'cid'                     => $cid ? Hashids::encode($cid) : '',
            'invite_code'             => $invite_code,
            'promotion_code'          => $promotion_code,
            'send_order_id'           => $send_order_id,
            'distribution_channel_id' => $distribution_channel_id,
        ];
        if ($send_order_id) $arr['jump'] = 'no';    // 增加阅读页跳转到选定章节的限制
        switch ($type) {
            case 'friend_url':      // 私信朋友
                if ($bid) {
                    if (!$cid) {    // 未设置章节则默认第一章
                        $first_cid  = Chapter::where('bid', $bid)->where(['is_check' => 1, 'is_deleted' => 0])->orderBy('sequence')->limit(1)->value('id');
                        $arr['cid'] = Hashids::encode($first_cid);
                    }
                    $arr['path'] = 'pages/reader/index';
                }
                break;
            case 'promotion_url':
                $arr['path'] = 'pages/detail/index';
                break;
            default:
                break;
        }

        // 统计(uv|pv)
        StatisticCache::setPV('set_url_link');
        if ($uid) StatisticCache::setUV('set_url_link', $uid);

        $path = $arr['path'];
        unset($arr['path']);
        return '/' . $path . '?' . http_build_query($arr);
    }

    /**
     * 生成分享链接(外网)
     *
     * @param $data
     * @return array|string
     * @throws \App\Exceptions\ApiException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function setDyLink($data)
    {
        $type                    = getProp($data, 'type');
        $send_order_id           = getProp($data, 'send_order_id');
        $distribution_channel_id = getProp($data, 'distribution_channel_id');
        $template_id             = getProp($data, 'template_id', env('SHARE_TEMPLATE_ID'));
        $uid                     = Site::getUid();
        $invite_code             = User::where('id', $uid)->value('invite_code');
        if (!$invite_code) $invite_code = '';
        $data['invite_code'] = $invite_code;
        Log::info('外网分享链接参数: ' . json_encode($data, 256));

        $bid = getProp($data, 'bid');
        $cid = getProp($data, 'cid');
        if (mb_strlen($bid) === 32) {
            $bid = Hashids::decode($bid)[0];
        }
        if (mb_strlen($cid) === 32) {
            $cid = Hashids::decode($cid)[0];
        }

        // 如果有派单链接,则替换bid和cid
        if ($send_order_id) {
            $send_order = DB::table('send_orders')->where('id', $send_order_id)->select('distribution_channel_id', 'book_id', 'chapter_id')->first();
            if ($send_order) {
                $bid                     = getProp($send_order, 'book_id');
                $cid                     = getProp($send_order, 'chapter_id');
                $distribution_channel_id = getProp($send_order, 'distribution_channel_id');
            }
        }

        $promotion_code = '';
        if ($bid) $promotion_code = DB::table('user_book_code')->where(['bid' => $bid, 'uid' => $uid])->value('id');

        $arr = [
            'path'                    => 'pages/index/index',
            'bid'                     => $bid ? Hashids::encode($bid) : '',
            'cid'                     => $cid ? Hashids::encode($cid) : '',
            'invite_code'             => $invite_code,
            'promotion_code'          => $promotion_code,
            'send_order_id'           => $send_order_id,
            'distribution_channel_id' => $distribution_channel_id,
        ];
        if ($send_order_id) $arr['jump'] = 'no';    // 增加阅读页跳转到选定章节的限制
        switch ($type) {
            case 'friend_url':      // 私信朋友
                if ($bid) {
                    if (!$cid) {    // 未设置章节则默认第一章
                        $first_cid  = Chapter::where('bid', $bid)->where(['is_check' => 1, 'is_deleted' => 0])->orderBy('sequence')->limit(1)->value('id');
                        $arr['cid'] = Hashids::encode($first_cid);
                    }
                    $arr['path'] = 'pages/reader/index';
                }
                break;
            case 'promotion_url':
                $arr['path'] = 'pages/detail/index';
                break;
            default:
                break;
        }

        // 统计(uv|pv)
        StatisticCache::setPV('set_dy_link');
        if ($uid) StatisticCache::setUV('set_dy_link', $uid);

        $path = $arr['path'];
        unset($arr['path']);
        $json = json_encode($arr, 256);

        // 调用抖音三方库生成分享链接
        $instance = $this->openService->getInstance(['sandbox' => env('DOUYIN_APP_SANDBOX')]);
        $urlLink  = $instance->generateShareLink($path, $json);
        if (isset($urlLink['url_link']) && $send_order_id) {
            DB::table('send_orders')->where('id', $send_order_id)->update([
                'send_order_url' => $urlLink['url_link'],
                'updated_at'     => date('Y-m-d H:i:s'),
            ]);
        }
        return $urlLink ? $urlLink : ['err_no' => 1, 'err_tips' => '', 'url_link' => ''];
    }

    /**
     * 轮播图
     *
     * @param $gender
     * @return mixed
     */
    public function getBanners($gender)
    {
        return Book::rightJoin('home_book_configs', 'home_book_configs.bid', 'books.zw_id')
                   ->leftJoin('book_configs', 'books.id', 'book_configs.bid')->whereIn('book_configs.is_on_shelf', [1, 2])
                   ->where('home_book_configs.type', 'banner')->where('home_book_configs.gender', $gender)
                   ->select('books.id', 'books.cover', 'books.name', 'books.intro')->get()->toArray();
    }

    /**
     * 获取原创精选书籍
     *
     * @param $gender
     * @return mixed
     */
    public function getFeatureBooks($gender)
    {
        return Book::rightJoin('home_book_configs', 'home_book_configs.bid', 'books.zw_id')
                   ->leftJoin('book_configs', 'books.id', 'book_configs.bid')->whereIn('book_configs.is_on_shelf', [1, 2])
                   ->where('home_book_configs.type', 'feature')->where('home_book_configs.gender', $gender)
                   ->select('books.id', 'books.name', 'books.cover', 'books.intro', 'books.gender', 'books.category_name')->get()->toArray();
    }

    /**
     * 获取当前阅读书籍
     *
     * @return array|mixed
     */
    public function getCurrentBook()
    {
        $uid = Site::getUid();
        if (!$uid) return [];
        return UserCache::getCurrentChapter($uid);
    }

    /**
     * 获取猜你喜欢书籍
     *
     * @param $gender
     * @return mixed
     */
    public function getFavoriteBooks($gender)
    {
        $uid          = Site::getUid();
        $channel_type = Site::getChannelType();
        $result       = [];

        // 获取当前阅读的redis数据
        $recent_books = UserCache::getRecentBooksKeys($uid);

        // 屏蔽首页banner图和精选好书模块的书籍(避免重复)
        $filter_bids = DB::table('home_book_configs')->where('gender', $gender)->select('bid')->get()->pluck('bid')->toArray();

        if ($recent_books) {        // 有分类则推荐分类书籍
            $recent_categories = DB::table('books')->whereIn('id', $recent_books)->get()->pluck('category_id')->toArray();

            $query = Book::leftJoin('book_configs', 'books.id', 'book_configs.bid')
                         ->whereIn('book_configs.is_on_shelf', [1, 2])->whereNotIn('books.zw_id', $filter_bids)
                         ->select('books.id', 'books.name', 'books.intro', 'books.cover', 'books.author', 'books.category_name',
                             'books.gender')->whereIn('books.category_id', array_unique($recent_categories));
            // 会员制站点只展示短篇书籍
//            if ($channel_type == 'PERIOD') {
//                $query->where('book_configs.charge_type', 'BOOK');
//            }

            $result = $query->where('books.gender', $gender)->paginate();
        }

        // 无阅读分类则推荐默认书单
        if (!$result) {
            $query = Book::leftJoin('book_configs', 'books.id', 'book_configs.bid')
                         ->whereIn('book_configs.is_on_shelf', [1, 2])->whereNotIn('books.zw_id', $filter_bids)
                         ->select('books.id', 'books.name', 'books.intro', 'books.cover', 'books.author', 'books.category_name',
                             'books.gender');
            // 会员制站点只展示短篇书籍
//            if ($channel_type == 'PERIOD') {
//                $query->where('book_configs.charge_type', 'BOOK');
//            }

            $result = $query->where('books.gender', $gender)->paginate();
        }
        return $result;
    }

    /**
     * 获取充值前20书籍
     *
     * @param $category_id
     * @return mixed
     */
    public function getBookOrderByCharge($category_id)
    {
        $channel_type = Site::getChannelType();
        $query        = Book::leftJoin('book_configs', 'books.id', 'book_configs.bid')
                            ->whereIn('book_configs.is_on_shelf', [1, 2])
                            ->select('books.id', 'books.name', 'books.intro', 'books.cover', 'books.author', 'books.category_name', 'books.gender',
                                DB::raw("(select sum(price) from orders where from_bid = books.id and status = 'PAID') as all_price"));
        // 会员制站点只展示短篇书籍
//        if ($channel_type == 'PERIOD') {
//            $query->where('book_configs.charge_type', 'BOOK');
//        }

        return $query->where('books.category_id', $category_id)->orderBy('all_price', 'desc')->limit(20)->get()->toArray();

    }

    /**
     * 章节目录
     *
     * @param $data
     * @return mixed
     */
    public function getChapterList($data)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $bid = getProp($data, 'bid');
        if (!$bid) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        $order_type  = getProp($data, 'order_type', 'asc');
        $page_number = getProp($data, 'page_number');

        $query = Chapter::where(['bid' => $bid, 'is_check' => 1, 'is_deleted' => 0])->select('id', 'name', 'charge_length', 'size', 'is_vip', 'sequence');

        if ($page_number) {
            $chapters = $query->orderBy('sequence', $order_type)->paginate($page_number);
        } else {
            $chapters = $query->orderBy('sequence', $order_type)->paginate(10);
        }

        if ($chapters) {
            $result['chapters'] = $chapters;
        } else {
            $result['chapters'] = [];
        }

        // 获取书籍信息
        $result['book'] = Book::leftJoin('book_configs', 'book_configs.bid', 'books.id')->where(['books.id'=>$bid])
            ->select('books.id as bid', 'book_configs.book_name', 'books.cover', 'books.size', 'books.chapter_count',
                'books.intro', 'books.category_name', 'book_configs.feed_advertise_seq', 'book_configs.follow_seq')->first();
        if (!$result['book']) {
            Utils::throwError(ErrorConst::BOOK_NOT_EXIST);
        }
        $result['book'] = $result['book']->toArray();

        // 获取书籍收费方式
        $book_charge_type = DB::table('channel_book_charge_type')->where('distribution_channel_id', $distribution_channel_id)->where('bid', $result['book']['bid'])->first();
        if ($book_charge_type) {
            $result['book']['charge_info'] = [
                'distribution_channel_id'   => getProp($book_charge_type, 'distribution_channel_id'),
                'book_calculate_price_type' => getProp($book_charge_type, 'book_calculate_price_type'),
                'book_coin'                 => getProp($book_charge_type, 'book_coin'),
            ];
        }else {
            $result['book']['charge_info'] = [];
        }

        $result['book']['cover'] = addPrefix($result['book']['cover']);
        $result['book']['intro'] = filterIntro($result['book']['intro']);

        return $result;
    }

    /**
     * 设置书籍收费方式
     * @param $data
     * @return bool
     * @throws \App\Exceptions\ApiException
     */
    public function setBookChargeType($data) {
        $distribution_channel_id = Site::getCurrentChannelId();
        $bid = getProp($data, 'bid');
        $book_calculate_price_type = getProp($data, 'book_calculate_price_type');
        $book_coin = getProp($data, 'book_coin');
        if (!$bid || !$book_calculate_price_type || !$book_coin) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        return DB::table('channel_book_charge_type')->updateOrInsert([
            'distribution_channel_id'   => $distribution_channel_id,
            'bid'                       => $bid
        ], [
            'book_calculate_price_type' => $book_calculate_price_type,
            'book_coin'                 => $book_coin,
            'created_at'                => date('Y-m-d H:i:s'),
            'updated_at'                => date('Y-m-d H:i:s'),
        ]);
    }

    /**
     * 章节信息
     *
     * @param $data
     * @return array
     */
    public function getChapterInfo($data)
    {
        $cid = getProp($data, 'cid');
        if (!$cid) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        $result = Chapter::leftJoin('chapter_contents', 'chapter_contents.id', 'chapters.chapter_content_id')
                         ->leftJoin('books', 'books.id', 'chapters.bid')
                         ->leftJoin('book_configs', 'book_configs.bid', 'chapters.bid')
                         ->whereIn('book_configs.is_on_shelf', [1, 2])
                         ->where('chapters.id', $cid)->where(['chapters.is_check' => 1, 'chapters.is_deleted' => 0])
                         ->select('chapters.id as cid', 'chapters.bid', 'chapters.name as chapter_name', 'chapters.sequence', 'books.gender',
                             'chapter_contents.content as chapter_content', 'chapters.is_vip', 'books.name as book_name', 'books.cover', 'book_configs.charge_type')->first();
        if (!$result) {
            Utils::throwError(ErrorConst::CHAPTER_NOT_EXIST);
        }

        $chapter = $result->toArray();

        $chapter['cover'] = addPrefix($chapter['cover']);
        // 获取上一章和下一章cid
        $prev_cid                   = Chapter::where('bid', $chapter['bid'])->where(['is_check' => 1, 'is_deleted' => 0])
                                             ->where('sequence', '<', $chapter['sequence'])->orderBy('sequence', 'desc')->limit(1)->value('id');
        $next_cid                   = Chapter::where('bid', $chapter['bid'])->where(['is_check' => 1, 'is_deleted' => 0])
                                             ->where('sequence', '>', $chapter['sequence'])->orderBy('sequence', 'asc')->limit(1)->value('id');
        $chapter['prev_cid']        = $prev_cid ? $prev_cid : 0;
        $chapter['next_cid']        = $next_cid ? $next_cid : 0;
        $chapter['chapter_content'] = filterContent($chapter['chapter_content']);

        return $chapter;
    }

    /**
     * 获取全部一级分类
     *
     * @param $data
     * @return mixed
     */
    public function getCategoryList($data)
    {
        $gender = trim(getProp($data, 'gender'));
        $uid    = Site::getUid();
        // 获取当前阅读书籍
        $current_chapter = UserCache::getCurrentChapter($uid);
        if (!$gender) $gender = $current_chapter['gender'] ? $current_chapter['gender'] : 1;
        return Category::where(['channel_id' => $gender, 'is_show' => 1])->where('pid', 0)
                       ->select('id as category_id', 'category_name')->get()->toArray();
    }

    /**
     * 获取分类书籍
     *
     * @param $data
     * @return mixed
     */
    public function getCategoryBooks($data)
    {
        $gender       = trim(getProp($data, 'gender'));
        $category_id  = getProp($data, 'category_id');
        $search_key   = getProp($data, 'search_key');
        $status       = getProp($data, 'status');
        $page_number  = getProp($data, 'page_number');
        $uid          = Site::getUid();
        $channel_type = Site::getChannelType();

        // 获取当前阅读书籍
        $current_chapter = UserCache::getCurrentChapter($uid);
        if (!$gender) $gender = $current_chapter['gender'] ? $current_chapter['gender'] : 1;

        $query = Book::leftJoin('book_configs', 'books.id', 'book_configs.bid')->whereIn('book_configs.is_on_shelf', [1, 2])
                     ->select('books.id', 'books.name', 'books.intro', 'books.cover', 'books.author', 'books.category_name', 'books.gender',
                         DB::raw("(select sum(price) from orders where from_bid = books.id and status = 'PAID') as all_price"))
                     ->where('books.gender', $gender);

        // 会员制站点只展示短篇书籍
//        if ($channel_type = 'PERIOD') {
//            $query->where('book_configs.charge_type', 'BOOK');
//        }

        if ($search_key) {
            // 书名或分类名模糊搜索
            if ($search_key == '其他') {      // 其他分类按无分类算
                $query->where('books.name', 'like', '%' . $search_key . '%')->orWhere('books.category_name', '');
            } else {
                $query->where('books.name', 'like', '%' . $search_key . '%')->orWhere('books.category_name', 'like', '%' . $search_key . '%');
            }
        }

        if ($category_id) {
            $query->where('books.category_id', $category_id);
        }

        if ($status !== '') {
            $query->where('books.status', $status);
        }

        if ($page_number) {
            return $query->orderBy('all_price', 'desc')->paginate($page_number);
        } else {
            return $query->orderBy('all_price', 'desc')->paginate();
        }
    }

    /**
     * 生成派单链接
     *
     * @param $data
     * @throws \App\Exceptions\ApiException
     */
    public function setSendOrder($data)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $bid                     = getProp($data, 'bid');
        $cid                     = getProp($data, 'cid');
        $template_id             = getProp($data, 'template_id');
        $report_percent          = getProp($data, 'report_percent', 0);
        $send_order_name         = getProp($data, 'send_order_name');
        if (!is_numeric($report_percent) || $report_percent > 100) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        if ($template_id) {
            $template = DB::table('channel_templates')->where('id', $template_id)->where('distribution_channel_id', $distribution_channel_id)->first();
            if (!$template) {
                Utils::throwError('1002:该充值模板不存在!');
            }
        }

        $chapter = Chapter::leftJoin('books', 'books.id', 'chapters.bid')
                          ->leftJoin('book_configs', 'book_configs.bid', 'books.id')->whereIn('book_configs.is_on_shelf', [1, 2])
                          ->where(['chapters.id' => $cid, 'chapters.bid' => $bid, 'chapters.is_check' => 1, 'chapters.is_deleted' => 0])
                          ->select('chapters.bid as book_id', 'books.name as book_name', 'chapters.id as chapter_id', 'chapters.name as chapter_name')
                          ->orderBy('chapters.sequence')->first();

        if (!$chapter) Utils::throwError(ErrorConst::CHAPTER_NOT_EXIST);
        $channel_info = DB::table('distribution_channels')->where('id', $distribution_channel_id)->select('id', 'name', 'channel_type', 'pay_id')->first();
        if (!$channel_info) Utils::throwError(ErrorConst::CHANNEL_NOT_EXIST);
        if (DB::table('send_orders')->where('name', $send_order_name)->value('id')) {
            Utils::throwError(ErrorConst::SEND_ORDER_NAME_INVALID);
        }

        $insert_data                            = $chapter->toArray();
        $insert_data['name']                    = $send_order_name;
        $insert_data['distribution_channel_id'] = getProp($channel_info, 'id');
        $insert_data['channel_type']            = getProp($channel_info, 'channel_type');
        $insert_data['cost']                    = '0';
        $insert_data['pay_id']                  = getProp($channel_info, 'pay_id');
        $insert_data['report_percent']          = $report_percent;
        $insert_data['created_at']              = $insert_data['updated_at'] = $insert_data['send_time'] = date('Y-m-d H:i:s');
        if ($template_id) $insert_data['pay_id'] = $template_id;
        $send_order_id = DB::table('send_orders')->insertGetId($insert_data);

        // 调用抖音三方库生成分享链接(非本地模式调用)
        if ($send_order_id && env('APP_ENV') != 'local') {
            $arr = [
                'bid'                     => $bid ? Hashids::encode($bid) : '',
                'cid'                     => $cid ? Hashids::encode($cid) : '',
                'send_order_id'           => $send_order_id,
                'distribution_channel_id' => $distribution_channel_id,
                'jump'                    => 'no',
            ];

            $json = json_encode($arr, 256);
            $path = 'pages/reader/index';   // 固定页面(阅读页)

            $instance = $this->openService->getInstance(['sandbox' => env('DOUYIN_APP_SANDBOX')]);
            $urlLink  = $instance->generateShareLink($path, $json);
            if (!isset($urlLink['url_link'])) {
                Utils::throwError(ErrorConst::SYS_EXCEPTION);
            }

            return DB::table('send_orders')->where('id', $send_order_id)->update([
                'send_order_url' => $urlLink['url_link'],
                'updated_at'     => date('Y-m-d H:i:s')
            ]);
        }

        return $send_order_id;
    }

    /**
     * 计算扣减书币
     *
     * @param $bid
     * @param $charge_length
     * @param $user_vip_level
     * @return int
     */
    public function calcDeductCoin($bid, $charge_length, $user_vip_level = 0)
    {
        return $this->calNormalCoin($charge_length, $user_vip_level);
    }

    /**
     * 默认情况扣减书币的金额
     *
     * @param     $charge_length
     * @param int $user_vip_level
     * @return int
     */
    private function calNormalCoin($charge_length, $user_vip_level = 0): int
    {
        // 正常价
        $coin = ceil(($charge_length / 1000) * CoinConst::DEFAULT_CHAPTER_PRICE * CoinConst::PRICE_COIN_RATIO);
        switch ($user_vip_level) {
            // 初级vip 77折
            case BaseConst::JUNIOR_VIP_LEVEL:
                $coin *= CoinConst::JUNIOR_CHAPTER_DISCOUNT;
                break;
            // 高级vip 55折
            case BaseConst::SENIOR_VIP_LEVEL:
                $coin *= CoinConst::SENIOR_CHAPTER_DISCOUNT;
                break;
            default:
                break;
        }

        return (int)round($coin);
    }

    /**
     * 执行扣减书币
     *
     * @param $user_info //用户信息
     * @param $chapter_info //书籍章节信息
     * @param $cid //章节id
     * @param $coin //待扣减的书币
     */
    private function decreaseBookCoin($user_info, $chapter_info, $cid, $coin)
    {
        $origin_coin    = $coin;
        $balance        = getProp($user_info, 'balance');
        $charge_balance = getProp($user_info, 'charge_balance');
        $reward_balance = getProp($user_info, 'reward_balance');
        if ($coin > $balance) return false;
        $update_data = [
            'reward_balance' => $reward_balance,
            'charge_balance' => $charge_balance
        ];
        $balance     -= $coin;
        foreach ($update_data as $key => $val) {
            if ($coin <= 0) break;
            if ($coin > $val) {
                $coin -= $val;
                $val  = 0;
            } else {
                $val  -= $coin;
                $coin = 0;
            }
            $update_data[$key] = $val;
        }
        $update_data['balance']    = $balance;
        $update_data['updated_at'] = date('Y-m-d H:i:s');
        $decrease_charge_balance   = $charge_balance - $update_data['charge_balance'];
        $decrease_reward_balance   = $reward_balance - $update_data['reward_balance'];

        $chapter_order_data = [
            'uid'                     => getProp($user_info, 'id'),
            'fee'                     => $origin_coin,
            'cid'                     => $cid,
            'bid'                     => getProp($chapter_info, 'bid'),
            'distribution_channel_id' => Site::getChannelId(),
            'book_name'               => getProp($chapter_info, 'book_name'),
            'chapter_name'            => getProp($chapter_info, 'chapter_name'),
            'send_order_id'           => Site::getSendOrderId(),
            'charge_balance'          => $decrease_charge_balance,
            'reward_balance'          => $decrease_reward_balance,
            'created_at'              => date('Y-m-d H:i:s'),
            'updated_at'              => date('Y-m-d H:i:s')
        ];

        try {
            DB::beginTransaction();

            // 更新用户书币
            $boolen = User::where('id', getProp($user_info, 'id'))->update($update_data);
            if (!$boolen) {
                $update_data['uid'] = getProp($user_info, 'id');
                DB::rollBack();
                dLog('chapter_orders')->info('更新用户书币余额失败: ', $update_data);
                Utils::throwError(ErrorConst::DB_INVALID);
            }

            // 订阅记录写入redis
            $subscribe_data = [
                'bid'            => getProp($chapter_info, 'bid'),
                'day'            => date('Y-m-d'),
                'balance'        => getProp($chapter_order_data, 'fee'),
                'charge_balance' => getProp($chapter_order_data, 'charge_balance'),
                'reward_balance' => getProp($chapter_order_data, 'reward_balance'),
                'is_chapter'     => getProp($chapter_info, 'charge_type') == 'CHAPTER' ? 1 : 0,
                'count'          => 1,
                'uid'            => getProp($user_info, 'id'),
            ];
            $boolen1        = StatisticCache::setSubscribe($subscribe_data, date('Ymd'));
            if (!$boolen1) {
                DB::rollBack();
                dLog('chapter_orders')->info('添加临时订阅记录失败: ', $chapter_order_data);
                Utils::throwError(ErrorConst::DB_INVALID);
            }

            // 增加订阅记录
            $boolen2 = $this->chapterOrderService->addChapterOrder($chapter_order_data);
            if (!$boolen2) {
                DB::rollBack();
                dLog('chapter_orders')->info('添加订阅记录失败: ', $chapter_order_data);
                Utils::throwError(ErrorConst::DB_INVALID);
            }

        } catch (\Exception $e) {
            DB::rollBack();
            return false;
        }
        DB::commit();
        return true;
    }

//    /**
//     * 扣减书币执行
//     * @param $uid
//     * @param $coin
//     * @param $changeType
//     * @param $extra
//     * @return array|bool
//     * @throws ApiException
//     */
//    public function decreaseBookCoin($uid, $coin, $changeType, $extra = [])
//    {
//        // 1. 获取用户书币信息
//        $userCoin    = User::getUserCoin($uid, true);
//        $bookInfo    = getProp($extra, 'bookInfo', []);
//        $chapterInfo = getProp($extra, 'chapterInfo', []);
//        $bid         = getProp($extra, 'bid', 0);
//        $cid         = getProp($extra, 'cid', 0);
//
//        // 2. 扣费提示
//        if ($coin > $userCoin['total']) {
//            // 书币不够，弹框提示返回数据
//
//        }
//
//        // 3. 根据扣除规则，获取需要扣减的书币列表
//        [$updateData, $changeData] = $this->buildDecreaseData($uid, $coin, $userCoin);
//        [$amount, $awardTotal, $awardForever, $awardValid, $charge] = $this->filterFieldUpdate($changeData);
//
//        // 5. 开启事务，执行扣除（添加操作记录）
//        try {
//            DB::beginTransaction();
//
//            // 扣除书币
//            $upRes = true;
//            if ($updateData) {
//                foreach ($updateData as $data) {
//                    $res   = BookCoinLogs::getInstance($uid)->updateUserCoinLog($data['id'], $uid, $data['remain'], $data['updated_at']);
//                    $upRes = $upRes && $res;
//                }
//            }
//
//            $addRes   = false;
//            $multiRes = false;
//
//            // 扣除书币成功
//            if ($upRes) {
//                // 增加扣币记录
//                $addRes = $this->addOptToCoinLog([
//                    'uid'         => $uid,
//                    'crease_type' => CoinConst::DECREASE,
//                    'coin_type'   => '',
//                    'change'      => $coin,
//                    'change_type' => $changeType,
//                    'extra'       => json_encode([
//                        'updateData' => $changeData
//                    ])
//                ]);
//
//                // 批量更新用户相关计数
//                if ($amount) {
//                    $multiRes = User::multiUpdateUserStat($uid, [
//                        'balance_total'  => 'balance_total - ' . $amount, // 总书币
//                        'bonus_total'    => 'bonus_total - ' . $awardTotal, // 奖金币汇总
//                        'bonus_forever'  => 'bonus_forever - ' . $awardForever, // 永久奖金币
//                        'bonus_valid'    => 'bonus_valid - ' . $awardValid, // 有效期奖金币
//                        'charge_balance' => 'charge_balance - ' . $charge, // 充值书币
//                    ]);
//                } else {
//                    // 兼容限免
//                    $multiRes = true;
//                }
//            }
//
//            // 判断返回结果
//            if (!$upRes || !$addRes || !$multiRes) {
//                myLog('decreaseBookCoin')->info('rollback', compact('uid', 'upRes', 'addRes', 'multiRes'));
//                DB::rollback();
//                return false;
//            }
//
//            // 提交事务
//            DB::commit();
//        } catch (\Exception $e) {
//            myLog('decreaseBookCoin')->info('exception', [$e->getMessage(), $e->getTraceAsString()]);
//            DB::rollback();
//            return false;
//        }
//
//        return ['originCoinChange' => (int)$charge, 'awardCoinChange' => (int)$amount - (int)$charge];
//    }
}
