<?php

namespace App\Services\Order;

use App\Consts\BaseConst;
use App\Consts\ErrorConst;
use App\Dao\Order\OrderDao;
use App\Facade\Site;
use App\Models\Order\Order;
use App\Libs\Utils;
use App\Services\OpenApi\OpenService;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Response;
use Vinkla\Hashids\Facades\Hashids;
use App\Exceptions\ApiException;

class OrderService
{
    protected $orderDao;
    protected $openService;

    public function __construct(
        OrderDao    $orderDao,
        OpenService $openService
    )
    {
        $this->orderDao    = $orderDao;
        $this->openService = $openService;
    }

    public static function getByTradeNo($trade_no)
    {
        return Order::getByTradeNo($trade_no);
    }

    public static function getByTikTokOrderId($orderId)
    {
        return Order::getByTikTokOrderId($orderId);
    }

    public static function getOrderList($uid, $page_size)
    {
        return Order::getOrderList($uid, $page_size);
    }

    public static function getSuccessOrderList($uid, $page_size)
    {
        return Order::select('id', 'price', 'created_at', 'status', 'trade_no', 'order_type', 'pay_merchant_id')->where('uid', $uid)->where('status', 'PAID')->where('from_type', '!=', 'order_add')->orderBy('id', 'desc')->paginate($page_size);
    }

    /**
     * 获取充值订单
     *
     * @param $uid
     * @return mixed
     */
    public static function totalChargeList($uid)
    {
        return Order::join('products', 'orders.product_id', '=', 'products.id')
                    ->select('products.price', 'products.given')
                    ->where('orders.order_type', 'RECHARGE')
                    ->where('orders.status', 'PAID')
                    ->where('orders.uid', $uid)
                    ->get();
    }


    static function create_order($data)
    {
        $insert_data                            = array();
        $insert_data['uid']                     = isset($data['uid']) ? $data['uid'] : '';
        $insert_data['price']                   = isset($data['price']) ? $data['price'] : '';
        $insert_data['status']                  = isset($data['status']) ? $data['status'] : '';
        $insert_data['pay_num']                 = isset($data['pay_num']) ? $data['pay_num'] : '';
        $insert_data['product_id']              = isset($data['product_id']) ? $data['product_id'] : '';
        $insert_data['distribution_channel_id'] = isset($data['distribution_channel_id']) ? $data['distribution_channel_id'] : '';
        $insert_data['trade_no']                = isset($data['trade_no']) ? $data['trade_no'] : '';
        $insert_data['pay_merchant_source']     = isset($data['pay_merchant_source']) ? $data['pay_merchant_source'] : '';
        $insert_data['pay_merchant_id']         = isset($data['pay_merchant_id']) ? $data['pay_merchant_id'] : '';
        $insert_data['transaction_id']          = isset($data['transaction_id']) ? $data['transaction_id'] : '';
        $insert_data['from_bid']                = isset($data['from_bid']) ? $data['from_bid'] : '';
        $insert_data['order_type']              = isset($data['order_type']) ? $data['order_type'] : '';
        $insert_data['pay_end_at']              = isset($data['pay_end_at']) ? $data['pay_end_at'] : null;
        $insert_data['create_ip']               = isset($data['create_ip']) ? $data['create_ip'] : '';
        $insert_data['send_order_id']           = isset($data['send_order_id']) ? $data['send_order_id'] : '';
        $result                                 = Order::create_order($insert_data);

        return $result;
    }


    /**
     * 判断用户首充
     */
    public static function judgeUserFirstRecharge($uid)
    {
        $user_recharge = Order::where([
            ['status', '=', 'PAID'],
            ['uid', '=', $uid]
        ])->first();
        if (!$user_recharge) {
            return true;
        }
        return false;
    }

    /**
     * 是否是付费用户
     *
     * @param $uid
     * @return bool
     */
    public static function isPaidUser($uid)
    {
        if (empty($uid)) {
            return false;
        }
        $result = Order::select('id')->where('uid', $uid)->where('status', 'PAID')->first();
        if ($result && $result->id) {
            return true;
        }
        return false;
    }

    /**
     * 是否是付费用户
     *
     * @param $openid
     * @return bool
     */
    public static function isPaidUserByOpenid($openid)
    {
        if (empty($openid)) {
            return false;
        }
        $result = Order::join('users', 'users.id', '=', 'orders.uid')
                       ->select('orders.id')
                       ->where('users.openid', $openid)
                       ->where('orders.status', 'PAID')
                       ->first();
        if ($result && $result->id) {
            return true;
        }
        return false;
    }

    public static function getUserChargeTimes($uid)
    {
        return Order::getUserChargeTimes($uid);
    }

    public static function getChargeNum($uid)
    {
        return Order::where('uid', $uid)->where('status', 'PAID')->count();
    }


    public static function getChargeNumByOpenId($openid)
    {
        $res = Order::join('users', 'orders.uid', 'users.id')
                    ->where('users.openid', $openid)
                    ->where('orders.status', 'PAID')
                    ->sum('orders.price');
        return $res ? $res : 0;
    }


    // 订单明细
    public function getOrderData($data)
    {
        $send_order_id  = getProp($data, 'send_order_id');
        $trade_no       = getProp($data, 'trade_no');
        $transaction_id = getProp($data, 'transaction_id');
        $date_range     = getProp($data, 'date_range');

        $query = Order::where('status', 'PAID')->select('uid', 'price', 'distribution_channel_id', 'trade_no', 'transaction_id',
            'send_order_id', 'created_at', 'order_type');

        if ($send_order_id) {
            $query->where('send_order_id', $send_order_id);
        }

        if ($trade_no) {
            $query->where('trade_no', $trade_no);
        }

        if ($transaction_id) {
            $query->where('transaction_id', $transaction_id);
        }

        //时间周期筛选
        if ($date_range) {
            $date_range = explode(',', $date_range);
            if (count($date_range) == 2) {
                if ($date_range[0]) {
                    $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
                    $query->where('created_at', '>=', $start);
                }

                if ($date_range[1]) {
                    $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
                    $query->where('created_at', '<=', $end);
                }
            }
        }

        return $query->get();
    }

    /**
     * @param $data
     * @return mixed
     * @throws ApiException
     */
    public function orderList($data)
    {
        // 是否导出
        $export = (int)getProp($data, 'export');
        if ($export) {
            return $this->exportOrderList($data);
        }

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

    /**
     * @param $param
     * @return mixed
     * @throws ApiException
     */
    public function exportOrderList($param)
    {
        $param['all'] = 1;
        $result      = $this->orderListData($param);
        if ($result->isEmpty()) {
            Utils::throwError(ErrorConst::NO_DATA_FOR_EXPORT);
        }

        // 设置超时时间
        set_time_limit(0);

        // 组装下载数据
        $data = [];
        foreach ($result as $order) {
            // 支付状态说明
            $status     = getProp($order, 'status');
            $statusDesc = $status === 'PAID' ? '已支付' : '未支付';

            // 订单类型说明
            $orderType = getProp($order, 'order_type');
            switch ($orderType) {
                case 'YEAR':
                    $orderTypeDesc = '包年';
                    break;
                case 'QUARTER':
                    $orderTypeDesc = '包季';
                    break;
                case 'MONTH':
                    $orderTypeDesc = '包月';
                    break;
                case 'WEEK':
                    $orderTypeDesc = '包周';
                    break;
                case 'RECHARGE':
                    $orderTypeDesc = '普通充值';
                    break;
                default:
                    $orderTypeDesc = '其他充值';
                    break;
            }

            $data[] = [
                getProp($order, 'distribution_channel_id'),
                getProp($order, 'trade_no'),
                getProp($order, 'uid'),
                getProp($order, 'register_ip'),
                getProp($order, 'register_time'),
                trim($orderTypeDesc),
                trim(getProp($order, 'book_name')),
                trim(getProp($order, 'send_order_name')),
                getProp($order, 'send_order_id'),
                getProp($order, 'price'),
                $statusDesc,
                $order->created_at->format('Y-m-d H:i:s'),
                $order->updated_at->format('Y-m-d H:i:s'),
            ];
        }

        $headers = [
            '站点ID', '订单编号', '用户ID', '注册IP', '注册时间', '充值类型', '充值书籍',
            '派单名称', '派单ID', '充值金额(元)', '订单状态', '创建时间', '更新时间'
        ];

        // 导出csv格式
        exportCsv('订单-' . date("YmdHis"), $headers, $data);

        // exportExcel($data, $headers, '订单-' . date("YmdHis"));
        // exit();
    }

    /**
     * 订单数据
     *
     * @param $data
     * @return mixed
     * @throws ApiException
     */
    private function orderListData($data)
    {
        // 获取当前站点
        $channelId = Site::getCurrentChannelId();
        if (empty($channelId)) {
            Utils::throwError(ErrorConst::CHANNEL_ID_INVALID);
        }

        $uid             = Site::getUid();
        $sub_channel_ids = DB::table('channel_user_mappings')->where('master_uid', $uid)->select('sub_channel_id')->get()->pluck('sub_channel_id')->toArray();
        if ($sub_channel_ids) {     // 主账号
            // 未筛选推广员则取全站数据
            $channel_ids  = array_merge([$channelId], $sub_channel_ids);
            $promote_user = getProp($data, 'promote_user');
            if ($promote_user) {    // 推广员筛选
                // 如果推广员符合子账号条件则取对应子站点数据
                $distribution_channel_id = DB::table('channel_users')->leftJoin('distribution_channels', 'distribution_channels.channel_user_id', 'channel_users.id')
                                             ->where('channel_users.nickname', 'like', '%' . $promote_user . '%')->value('distribution_channels.id');
                if (in_array($distribution_channel_id, $channel_ids)) $channel_ids = [$distribution_channel_id];
                else $channel_ids = [-1];
            }
        } else {     // 默认获取本站点数据
            $channel_ids = [$channelId];
        }

        // 站点对应
        $data['channel_id'] = $channel_ids;

        // 返回数据
        return $this->orderDao->orderList($data);
    }

    // 用户数据
    public function getUserData($data)
    {
        $uid           = getProp($data, 'uid');
        $send_order_id = getProp($data, 'send_order_id');
        $from_uid      = getProp($data, 'from_uid');
        $date_range    = getProp($data, 'date_range');

        $query = DB::table('users')->select('id', 'distribution_channel_id', 'send_order_id', 'from_uid', 'balance', 'charge_balance',
            'reward_balance', 'nickname', 'vip_limit_date', 'token', 'created_at');

        if ($uid) {
            $query->where('id', $uid);
        }

        if ($send_order_id) {
            $query->where('send_order_id', $send_order_id);
        }

        if ($from_uid) {
            $query->where('from_uid', $from_uid);
        }

        //时间周期筛选
        if ($date_range) {
            $date_range = explode(',', $date_range);
            if (count($date_range) == 2) {
                if ($date_range[0]) {
                    $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
                    $query->where('created_at', '>=', $start);
                }

                if ($date_range[1]) {
                    $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
                    $query->where('created_at', '<=', $end);
                }
            }
        }

        return $query->get();
    }

    // 回传日志
    public function getReportData($data, $all = false)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $uid                     = getProp($data, 'uid');
        $send_order_id           = getProp($data, 'send_order_id');
        $advertiserid            = getProp($data, 'advertiser_id');
        $promotionid             = getProp($data, 'promotion_id');
        $register_date_range     = getProp($data, 'register_date_range');
        $pay_date_range          = getProp($data, 'pay_date_range');

        // 列表
        $query = DB::table('orders')->leftJoin('users', 'users.id', 'orders.uid')
                   ->leftJoin('dy_report_logs', 'dy_report_logs.trade_no', 'orders.trade_no')
                   ->leftJoin('send_orders', 'send_orders.id', 'orders.send_order_id')
                   ->where('send_orders.distribution_channel_id', $distribution_channel_id)
                   ->where('orders.status', 'PAID');

        // 汇总
        $query2 = DB::table('orders')->leftJoin('users', 'users.id', 'orders.uid')
                    ->leftJoin('dy_report_logs', 'dy_report_logs.trade_no', 'orders.trade_no')
                    ->leftJoin('send_orders', 'send_orders.id', 'orders.send_order_id')
                    ->where('send_orders.distribution_channel_id', $distribution_channel_id)
                    ->where('orders.status', 'PAID');

        if ($uid) {
            $query->where('orders.uid', $uid);
            $query2->where('orders.uid', $uid);
        }

        if ($send_order_id) {
            $query->where('orders.send_order_id', $send_order_id);
            $query2->where('orders.send_order_id', $send_order_id);
        }

        if ($advertiserid) {
            $query->where('dy_report_logs.advertiserid', $advertiserid);
            $query2->where('dy_report_logs.advertiserid', $advertiserid);
        }

        if ($promotionid) {
            $query->where('dy_report_logs.promotionid', $promotionid);
            $query2->where('dy_report_logs.promotionid', $promotionid);
        }

        // 注册时间周期筛选
        if ($register_date_range) {
            $date_range = explode(',', $register_date_range);
            if (count($date_range) == 2) {
                if ($date_range[0]) {
                    $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
                    $query->where('users.created_at', '>=', $start);
                    $query2->where('users.created_at', '>=', $start);
                }

                if ($date_range[1]) {
                    $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
                    $query->where('users.created_at', '<=', $end);
                    $query2->where('users.created_at', '<=', $end);
                }
            }
        }

        // 支付时间周期筛选
        if ($pay_date_range) {
            $date_range = explode(',', $pay_date_range);
            if (count($date_range) == 2) {
                if ($date_range[0]) {
                    $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
                    $query->where('orders.pay_end_at', '>=', $start);
                    $query2->where('orders.pay_end_at', '>=', $start);
                }

                if ($date_range[1]) {
                    $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
                    $query->where('orders.pay_end_at', '<=', $end);
                    $query2->where('orders.pay_end_at', '<=', $end);
                }
            }
        }

        if ($all) return $query->get();

        // 获取分页数据
        $result = $query->select('dy_report_logs.id', 'dy_report_logs.advertiserid', 'dy_report_logs.promotionid',
            'dy_report_logs.callback_result', 'dy_report_logs.remark', 'users.created_at as register_time',
            'orders.send_order_id', 'orders.trade_no', 'orders.uid', 'users.register_ip', 'orders.pay_end_at as pay_time', 'orders.price',
            'send_orders.report_percent', 'send_orders.report_receive_num', 'send_orders.report_post_num')
                        ->orderBy('orders.pay_end_at', 'desc')->orderBy('users.created_at', 'desc')->paginate();

        // 获取汇总数据
        $all_result = $query2->selectRaw("sum(orders.price) as total_pay_amount, count(orders.id) as order_num,
            (select count(o.id) from orders as o where o.pay_num = 1 and o.trade_no = orders.trade_no) as first_order_num,
            (select sum(o.price) from orders as o where o.pay_num = 1 and o.trade_no = orders.trade_no) as first_total_pay_amount")->groupBy('orders.trade_no')->get();
        $header     = [
            'total_pay_amount'       => 0,
            'order_num'              => 0,
            'first_order_num'        => 0,
            'first_total_pay_amount' => 0,
        ];
        foreach ($all_result as $item) {
            $header['total_pay_amount']       += getProp($item, 'total_pay_amount') ? getProp($item, 'total_pay_amount') : 0;
            $header['order_num']              += getProp($item, 'order_num');
            $header['first_order_num']        += getProp($item, 'first_order_num');
            $header['first_total_pay_amount'] += getProp($item, 'first_total_pay_amount') ? getProp($item, 'first_total_pay_amount') : 0;
        }
        $header['total_pay_amount']       = round($header['total_pay_amount'], 2);
        $header['first_total_pay_amount'] = round($header['first_total_pay_amount'], 2);
        $result->header                   = $header;

        return $result;
    }

    /**
     * 导出回传日志
     *
     * @param $data
     * @throws ApiException
     */
    public function exportReportData($data)
    {
        $list = $this->getReportData($data, true);
        if (empty($list)) {
            Utils::throwError(ErrorConst::RECORD_NOT_EXIST);
        }

        $result = [];
        foreach ($list as $item) {
            $callback_result = getProp($item, 'callback_result');
            $remark          = getProp($item, 'remark');
            $remark          = $remark == '' ? '比例回传' : $remark;
            $result[]        = [
                'report_id'       => getProp($item, 'id'),
                'advertiserid'    => getProp($item, 'advertiserid'),
                'promotionid'     => getProp($item, 'promotionid'),
                'callback_result' => $callback_result,
                'remark'          => $remark,
                'uid'             => getProp($item, 'uid'),
                'register_ip'     => getProp($item, 'register_ip'),
                'register_time'   => transDate(getProp($item, 'register_time')),
                'pay_time'        => transDate(getProp($item, 'pay_time')),
                'price'           => getProp($item, 'price'),
                'current_percent' => getProp($item, 'report_receive_num') ? sprintf('%.1f', getProp($item, 'report_post_num') / getProp($item, 'report_receive_num') * 100) : 0,
                'report_percent'  => getProp($item, 'report_percent'),
            ];
        }

        $headers = ['ID', '广告账户ID', '广告ID', '回传状态', '备注', '用户ID', '注册IP', '注册时间', '充值时间', '充值金额', '已回传/待回传(%)', '应用回传比例(%)'];
        exportFileCsv($headers, $result, '回传日志-' . date('YmdHis'));
    }

    // 派单列表
    public function getSendOrderData($data, $all = false)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $send_order_id           = getProp($data, 'send_order_id');
        $send_order_name         = getProp($data, 'send_order_name');
        $date_range              = getProp($data, 'date_range');

        $uid             = Site::getUid();
        $sub_channel_ids = DB::table('channel_user_mappings')->where('master_uid', $uid)->select('sub_channel_id')->get()->pluck('sub_channel_id')->toArray();
        if ($sub_channel_ids) {     // 主账号
            // 未筛选推广员则取全站数据
            $channel_ids  = array_merge([$distribution_channel_id], $sub_channel_ids);
            $promote_user = getProp($data, 'promote_user');
            if ($promote_user) {    // 推广员筛选
                // 如果推广员符合子账号条件则取对应子站点数据
                $distribution_channel_id = DB::table('channel_users')->leftJoin('distribution_channels', 'distribution_channels.channel_user_id', 'channel_users.id')
                                             ->where('channel_users.nickname', 'like', '%' . $promote_user . '%')->value('distribution_channels.id');
                if (in_array($distribution_channel_id, $channel_ids)) $channel_ids = [$distribution_channel_id];
                else $channel_ids = [-1];
            }
        } else {     // 默认获取本站点数据
            $channel_ids = [$distribution_channel_id];
        }

        $query = DB::table('send_orders')->leftJoin('books', 'books.id', 'send_orders.book_id')
                   ->leftJoin('distribution_channels', 'distribution_channels.id', 'send_orders.distribution_channel_id')
                   ->leftJoin('channel_users', 'channel_users.id', 'distribution_channels.channel_user_id')
                   ->leftJoin('channel_templates', 'channel_templates.id', 'send_orders.pay_id')
                   ->whereIn('send_orders.distribution_channel_id', $channel_ids)->select('send_orders.*', 'books.cover', 'books.category_name', 'books.intro',
                'channel_templates.template_name', 'channel_templates.id as template_id', 'channel_users.nickname');

        if ($send_order_id) {
            $query->where('send_orders.id', $send_order_id);
        }

        if ($send_order_name) {
            $query->where('send_orders.name', 'like', '%' . $send_order_name . '%');
        }

        // 支付时间周期筛选
        if ($date_range) {
            $date_range = explode(',', $date_range);
            if (count($date_range) == 2) {
                if ($date_range[0]) {
                    $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
                    $query->where('send_orders.created_at', '>=', $start);
                }

                if ($date_range[1]) {
                    $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
                    $query->where('send_orders.created_at', '<=', $end);
                }
            }
        }

        if ($all) return $query->orderBy('send_orders.created_at', 'desc')->get();

        $result             = $query->orderBy('send_orders.created_at', 'desc')->paginate();
        $result->channel_id = $distribution_channel_id;

        return $result;
    }

    // 导出派单数据
    public function exportSendOrderData($data)
    {
        $list = $this->getSendOrderData($data, true);
        if (empty($list)) {
            Utils::throwError(ErrorConst::RECORD_NOT_EXIST);
        }

        $start = date('Y-m-d 00:00:00');

        $result = [];
        foreach ($list as $item) {
            $send_order_id = getProp($item, 'id');
            // 获取今日注册人数
            $today_register_num = DB::table('users')->where('send_order_id', $send_order_id)->where('created_at', '>', $start)->count('id');

            // 今日订单信息
            $today_order               = DB::table('orders')->leftJoin('users', 'orders.uid', 'users.id')->where('users.send_order_id', $send_order_id)
                                           ->where('orders.send_order_id', $send_order_id)->where('users.created_at', '>', $start)
                                           ->where('orders.status', 'PAID')->where('orders.created_at', '>', $start)->selectRaw("count(orders.pay_num = 1) as register_pay_num, sum(orders.price) as register_pay_amount")->get();
            $today_register_pay_num    = getProp($today_order[0], 'register_pay_num');
            $today_register_pay_amount = getProp($today_order[0], 'register_pay_amount') ? getProp($today_order[0], 'register_pay_amount') : 0;

            // 派单uv
            $uv = Redis::scard('send_order_uv_' . $send_order_id);
            $uv = $uv ? $uv : 0;

            // 累计订单信息
            $total_order      = DB::table('orders')->where('orders.send_order_id', $send_order_id)->where('orders.status', 'PAID')
                                  ->selectRaw("count(distinct orders.uid) as pay_num, sum(orders.price) as pay_amount")->get();
            $total_pay_num    = getProp($total_order[0], 'pay_num');
            $total_pay_amount = getProp($total_order[0], 'pay_amount') ? getProp($total_order[0], 'pay_amount') : 0;

            $result[] = [
                'send_order_id'             => $send_order_id,
                'send_order_name'           => getProp($item, 'name'),
                'book_name'                 => getProp($item, 'book_name'),
                'chapter_name'              => getProp($item, 'chapter_name'),
                'today_register_num'        => $today_register_num,
                'today_register_pay_num'    => $today_register_pay_num,
                'today_register_pay_amount' => $today_register_pay_amount,
                'total_register_num'        => getProp($item, 'total_register_num') + $today_register_num,
                'total_register_pay_num'    => getProp($item, 'total_register_pay_num') + $today_register_pay_num,
                'total_register_pay_amount' => getProp($item, 'total_register_pay_amount') + $today_register_pay_amount,
                'uv'                        => $uv,
                'total_pay_num'             => $total_pay_num,
                'total_pay_amount'          => $total_pay_amount,
                'total_cost'                => getProp($item, 'cost'),
                'total_profit'              => getProp($item, 'total_profit'),
                'total_profit_percent'      => (float)getProp($item, 'cost') ? round($total_pay_amount / getProp($item, 'cost') * 100, 2) : 0,
                'created_at'                => transDate(getProp($item, 'created_at')),
            ];
        }

        $headers = ['派单id', '派单名称', '书名', '章节名', '今日新增注册', '今日注册充值人数', '今日注册用户充值(元)', '累计注册', '累计注册充值人数', '累计注册用户充值(元)', 'UV', '累计充值人数', '累计充值金额(元)', '累计成本(元)', '累计盈利(元)', '累计回本(%)', '创建时间'];
        exportFileCsv($headers, $result, '派单数据-' . date('YmdHis'));
    }

    /**
     * 获取派单链接适用的模板
     *
     * @param $data
     * @return array
     */
    public function getSendOrderTemplates($data)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        return DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('is_enable', 1)->whereIn('user_scope', [1, 3])
                 ->select('id as template_id', 'template_name')->get()->map(function ($value) {
                return (array)$value;
            })->toArray();
    }

    /**
     * 编辑派单链接
     *
     * @param $data
     * @return mixed
     * @throws \App\Exceptions\ApiException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function editSendOrderUrl($data)
    {
        $send_order_id   = getProp($data, 'send_order_id');
        $send_order_name = getProp($data, 'send_order_name');
        $report_percent  = getProp($data, 'report_percent', 0);
        $template_id     = getProp($data, 'template_id');
        if (!is_numeric($report_percent)) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }
        // 获取派单信息
        $send_order = DB::table('send_orders')->where('id', $send_order_id)->first();
        if (!$send_order) {
            Utils::throwError(ErrorConst::SEND_ORDER_NOT_EXIST);
        }
        $distribution_channel_id = getProp($send_order, 'distribution_channel_id');
        if ((int)$distribution_channel_id !== (int)Site::getCurrentChannelId()) {
            Utils::throwError(ErrorConst::CHANNEL_NOT_EXIST);
        }
        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:该充值模板不存在!');
            }
        }

        // 判断派单名称是否重复
        $id = DB::table('send_orders')->where('name', $send_order_name)->value('id');
        if ($id && $id != $send_order_id) {
            Utils::throwError(ErrorConst::SEND_ORDER_NAME_INVALID);
        }

        // 更新派单信息
        $update_data = [
            'name'           => $send_order_name,
            'report_percent' => $report_percent,
            'updated_at'     => date('Y-m-d H:i:s')
        ];
        if (getProp($send_order, 'report_percent') != $report_percent) {
            $update_data['report_receive_num'] = 0;
            $update_data['report_post_num']    = 0;
        }
        if ($template_id) {
            $update_data['pay_id'] = $template_id;
        }

        // 调用抖音三方库生成分享链接(非本地模式调用)
        if (env('APP_ENV') != 'local') {
            $bid                     = getProp($send_order, 'book_id');
            $cid                     = getProp($send_order, 'chapter_id');
            $distribution_channel_id = getProp($send_order, 'distribution_channel_id');

            // 重新生成派单链接
            $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);
            }
            $update_data['send_order_url'] = $urlLink['url_link'];
        }

        return DB::table('send_orders')->where('id', $send_order_id)->update($update_data);
    }

    // 派单日数据
    public function getSendOrderDayData($data, $all = false)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $send_order_id           = getProp($data, 'send_order_id');
        $date_range              = getProp($data, 'date_range');

        // 获取主账号下的子账号站点
        $uid             = Site::getUid();
        $sub_channel_ids = DB::table('channel_user_mappings')->where('master_uid', $uid)->select('sub_channel_id')->get()->pluck('sub_channel_id')->toArray();

        $send_order_info = DB::table('send_orders')->leftJoin('books', 'books.id', 'send_orders.book_id')
                             ->leftJoin('book_configs', 'book_configs.bid', 'send_orders.book_id')
                             ->leftJoin('books as b1', 'b1.id', 'book_configs.origin_bid')
                             ->where('send_orders.id', $send_order_id)->select('send_orders.id', 'send_orders.name', 'send_orders.distribution_channel_id',
                'books.name as book_name', 'b1.name as origin_book_name')->first();
        if (!$send_order_info) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }
        $send_order_name       = getProp($send_order_info, 'name');
        $send_order_channel_id = getProp($send_order_info, 'distribution_channel_id');
        if (!in_array($send_order_channel_id, $sub_channel_ids) && $send_order_channel_id != $distribution_channel_id) {
            Utils::throwError('1002: 没有该站点的查看权限!');
        }

        $query = DB::table('send_order_day_stats')->where('send_order_id', $send_order_id);

        // 默认时间范围是最近一个月
        $end   = date('Y-m-d', strtotime('-1 day'));
        $start = date('Y-m-d', strtotime('-31 day'));

        if ($date_range) {
            $date_range = explode(',', $date_range);
            if (count($date_range) == 2) {
                $start = $date_range[0];
                $end   = $date_range[1];
                if (strtotime($end) - strtotime($start) > 86400 * 30) {
                    Utils::throwError(ErrorConst::DATE_RANGE_OVER_MONTH);
                }
            }
        }
        $query->whereBetween('day', [$start, $end]);

        if ($all) {
            $result = $query->orderBy('day', 'desc')->get();
            return [$result, $start, $send_order_info];
        }

        $result                  = $query->orderBy('day', 'desc')->paginate(5);
        $result->send_order_name = $send_order_name;
        $result->start_date      = $start;
        $result->send_order_id   = $send_order_id;

        return $result;
    }

    // 导出派单日数据
    public function exportSendOrderDayData($data)
    {
        [$list, $start, $send_order_info] = $this->getSendOrderDayData($data, true);
        if (empty($list)) {
            Utils::throwError(ErrorConst::RECORD_NOT_EXIST);
        }
        $send_order_id   = getProp($send_order_info, 'id');
        $send_order_name = getProp($send_order_info, 'name');

        $end = date('Y-m-d', strtotime($start . ' +120 day'));

        // 获取时间段内的数据
        $period_data = DB::table('send_order_recharge_day_stats')->where('send_order_id', $send_order_id)->whereBetween('register_date', [$start, $end])
                         ->orderBy('pay_date')->get()->map(function ($value) {
                return (array)$value;
            })->toArray();

        $day_stats = [];

        // 注册用户派单日数据初始化
        foreach ($list as $item) {
            $day_stats[getProp($item, 'day')] = [];
        }
        // 将已有数据填充
        foreach ($period_data as $item) {
            $day                                     = $item['register_date'];
            $day_stats[$day]['t' . $item['day_num']] = [
                'register_pay_amount'       => $item['pay_amount'],
                'total_register_pay_amount' => $item['day_num'] == 0 ? $item['pay_amount'] : 0,     // t0日有金额,其他日在下个循环累加
            ];
        }
        $i_arr = range(0, 29);                            // 第1~30天
        $i_arr = array_merge($i_arr, [44, 59, 89]);       // 第45、60、90天

        // 将当日无数据的日期占位并计算累计值
        foreach ($day_stats as $day => $value) {
            $today_start = date('Y-m-d 00:00:00');
            $today_end   = date('Y-m-d 23:59:59');
            // 获取当日注册用户
            if (Redis::exists('register_uids_' . $send_order_id . '_' . $day)) {      //获取缓存
                $day_register_uids = json_decode(Redis::get('register_uids_' . $send_order_id . '_' . $day), true);
            } else {
                $day_register_uids = DB::table('users')->where('send_order_id', $send_order_id)
                                       ->whereBetween('created_at', [date('Y-m-d 00:00:00', strtotime($day)), date('Y-m-d 23:59:59', strtotime($day))])
                                       ->select('id')->get()->pluck('id')->toArray();

                // 写入缓存
                Redis::set('register_uids_' . $send_order_id . '_' . $day, json_encode($day_register_uids));
                Redis::expire('register_uids_' . $send_order_id . '_' . $day, strtotime($today_end) - time());
            }

            // 获取注册用户今日数据
            $day_stats[$day]['today']['register_pay_amount']       = DB::table('orders')->where('send_order_id', $send_order_id)->where('status', 'PAID')
                                                                       ->whereIn('uid', $day_register_uids)->whereBetween('created_at', [$today_start, $today_end])->sum('price');
            $day_stats[$day]['today']['total_register_pay_amount'] = DB::table('orders')->where('send_order_id', $send_order_id)->where('status', 'PAID')
                                                                       ->whereIn('uid', $day_register_uids)->where('created_at', '<=', $today_end)->sum('price');

            // 获取注册用户总数据
            $total_data                                   = DB::table('orders')->where('send_order_id', $send_order_id)->where('status', 'PAID')
                                                              ->whereIn('uid', $day_register_uids)->selectRaw("count(distinct uid) as pay_num, sum(price) as total_pay_amount")->get();
            $day_stats[$day]['total']['pay_num']          = getProp($total_data[0], 'pay_num');
            $day_stats[$day]['total']['total_pay_amount'] = getProp($total_data[0], 'total_pay_amount') ? getProp($total_data[0], 'total_pay_amount') : 0;

            for ($i = 0; $i <= 89; $i++) {
                $diff_day = (strtotime(date('Y-m-d')) - strtotime($day)) / 86400;

                if (isset($day_stats[$day]['t' . $i])) {
                    if ($i != 0) $day_stats[$day]['t' . $i]['total_register_pay_amount'] = round($day_stats[$day]['t' . ($i - 1)]['total_register_pay_amount'] + $day_stats[$day]['t' . $i]['register_pay_amount'], 2);
                } else {
                    if ($i == $diff_day) {      // 如果是当日则取当日数据
                        $day_stats[$day]['t' . $i] = [
                            'register_pay_amount'       => $day_stats[$day]['today']['register_pay_amount'],
                            'total_register_pay_amount' => 0,
                        ];
                    } else {
                        $day_stats[$day]['t' . $i] = [
                            'register_pay_amount'       => 0,
                            'total_register_pay_amount' => 0,
                        ];
                    }

                    if ($i != 0) $day_stats[$day]['t' . $i]['total_register_pay_amount'] = round($day_stats[$day]['t' . ($i - 1)]['total_register_pay_amount'] + $day_stats[$day]['t' . $i]['register_pay_amount'], 2);
                }
            }
        }

        $result = [];
        foreach ($list as $item) {
            $send_order_id = getProp($item, 'send_order_id');
            $day           = getProp($item, 'day');
            $cost          = getProp($item, 'cost');

            // 获取日统计数据
            $stats = $day_stats[$day];

            $arr = [
                'distribution_channel_id'   => getProp($send_order_info, 'distribution_channel_id'),
                'send_order_id'             => $send_order_id,
                'send_order_name'           => $send_order_name,
                'book_name'                 => getProp($send_order_info, 'book_name'),
                'origin_book_name'          => getProp($send_order_info, 'origin_book_name'),
                'date'                      => $day,
                'cost'                      => $cost,
                'register_num'              => getProp($item, 'register_num'),                                                                                           // 注册用户
                'register_cost'             => getProp($item, 'register_num') ? round($cost / getProp($item, 'register_num'), 2) : 0,                                    // 注册成本
                'register_pay_num'          => getProp($item, 'register_pay_num'),                                                                                       // 当日注册充值人数
                'register_pay_percent'      => getProp($item, 'register_num') ? round(getProp($item, 'register_pay_num') / getProp($item, 'register_num') * 100, 2) : 0, // 当日注册付费率
                'register_pay_cost'         => getProp($item, 'register_pay_num') ? round($cost / getProp($item, 'register_pay_num'), 2) : 0,                            // 当日转化成本
                'total_register_pay_amount' => getProp($stats, 'total')['total_pay_amount'],                                                                             // 累计充值
                'total_profit_percent'      => (float)$cost ? round(getProp($stats, 'total')['total_pay_amount'] / $cost * 100, 2) : 0,                                  //累计回本率
                'total_profit_cost'         => getProp($stats, 'total')['pay_num'] ? round($cost / getProp($stats, 'total')['pay_num'], 2) : 0,
            ];
            foreach ($i_arr as $i) {
                $profit_percent                               = (float)$cost ? round($stats['t' . $i]['register_pay_amount'] / $cost * 100, 2) : 0;
                $total_profit_percent                         = (float)$cost ? round($stats['t' . $i]['total_register_pay_amount'] / $cost * 100, 2) : 0;
                $arr['t' . $i . '_register_pay_amount']       = $stats['t' . $i]['register_pay_amount'];
                $arr['t' . $i . '_total_register_pay_amount'] = $stats['t' . $i]['total_register_pay_amount'];
                $arr['t' . $i . '_profit_percent']            = $profit_percent;
                $arr['t' . $i . '_total_profit_percent']      = $total_profit_percent;
//                $arr['t'.$i] = '当日充值金额(元)： ' . $stats['t'.$i]['register_pay_amount'] . PHP_EOL .
//                    '累计充值金额(元)： ' . $stats['t'.$i]['total_register_pay_amount'] . PHP_EOL .
//                    '当日回本率(%)： '. $profit_percent . PHP_EOL .
//                    '累计回本率(%)： ' . $total_profit_percent;
            }

            $result[] = $arr;
        }

        $headers = ['站点ID', '派单ID', '派单名称', '推广书籍', '原书名', '日期', '成本', '注册用户', '注册成本', '当日注册充值人数', '当日注册付费率(%)', '当日转化成本(元)', '累计充值', '累计回本率(%)', '累计转化成本(元)', 'T+0(当日)当日充值金额(元)', 'T+0(当日)累计充值金额(元)', 'T+0(当日)当日回本率(%)', 'T+0(当日)累计回本率(%)'];
        for ($i = 1; $i <= 29; $i++) {
            $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)当日充值金额(元)';
            $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)累计充值金额(元)';
            $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)当日回本率(%)';
            $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)累计回本率(%)';
        }
        $headers = array_merge($headers, ['T+44(第45日)当日充值金额(元)', 'T+44(第45日)累计充值金额(元)', 'T+44(第45日)当日回本率(%)', 'T+44(第45日)累计回本率(%)', 'T+59(第60日)当日充值金额(元)', 'T+59(第60日)累计充值金额(元)', 'T+59(第60日)当日回本率(%)', 'T+59(第60日)累计回本率(%)', 'T+89(第90日)当日充值金额(元)', 'T+89(第90日)累计充值金额(元)', 'T+89(第90日)当日回本率(%)', 'T+89(第90日)累计回本率(%)']);

        exportFileCsv($headers, $result, '派单日数据-' . $send_order_name . '-' . date('YmdHis'));
    }

    /**
     * 设置派单日成本
     *
     * @param $data
     * @return mixed
     * @throws \App\Exceptions\ApiException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function setDayCost($data)
    {
        $id   = getProp($data, 'id');
        $cost = getProp($data, 'cost');
        if (!is_numeric($cost)) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        // 获取派单日信息
        $send_order_day_stats = DB::table('send_order_day_stats')->where('id', $id)->first();
        if (!$send_order_day_stats) {
            Utils::throwError(ErrorConst::SEND_ORDER_NOT_EXIST);
        }

        // 数据未变更则直接返回
        if (getProp($send_order_day_stats, 'cost') == $cost) {
            return true;
        }

        // 获取派单信息
        $send_order_id = getProp($send_order_day_stats, 'send_order_id');
        $send_order    = DB::table('send_orders')->where('id', $send_order_id)->first();
        if (!$send_order) {
            Utils::throwError(ErrorConst::SEND_ORDER_NOT_EXIST);
        }

        try {
            DB::beginTransaction();
            $boolen = DB::table('send_order_day_stats')->where('id', $id)->update([
                'cost'       => $cost,
                'updated_at' => date('Y-m-d H:i:s')
            ]);
            if (!$boolen) {
                DB::rollback();
                Log::info('派单日数据更新失败: ' . json_encode([
                        'id'         => $id,
                        'cost'       => $cost,
                        'updated_at' => date('Y-m-d H:i:s')
                    ]));
                Utils::throwError(ErrorConst::DB_INVALID);
            }

            $total_cost   = DB::table('send_order_day_stats')->where('send_order_id', $send_order_id)->sum('cost');
            $total_profit = getProp($send_order, 'total_register_pay_amount') - $total_cost;
            $boolen1      = DB::table('send_orders')->where('id', $send_order_id)->update([
                'cost'         => $total_cost,
                'total_profit' => $total_profit,
                'updated_at'   => date('Y-m-d H:i:s')
            ]);
            if (!$boolen1) {
                DB::rollback();
                Log::info('派单总数据更新失败: ' . json_encode([
                        'send_order_id' => $send_order_id,
                        'cost'          => $total_cost,
                        'total_profit'  => $total_profit,
                        'updated_at'    => date('Y-m-d H:i:s')
                    ]));
                Utils::throwError(ErrorConst::DB_INVALID);
            }
        } catch (\Exception $e) {
            DB::rollback();
            Utils::throwError(ErrorConst::STOP_TRANSACTION);
        }

        DB::commit();
        return true;
    }

    /**
     * 充值模板
     *
     * @param $data
     * @return mixed
     */
    public function getTemplateList($data)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $template_id             = getProp($data, 'template_id');

        $query = DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id);
        if ($template_id) {
            $query->where('id', $template_id);
        }
        return $query->orderBy('template_type')->orderBy('created_at', 'desc')->paginate();
    }

    /**
     * 设置充值模板启用状态
     *
     * @param $data
     * @return int
     * @throws ApiException
     */
    public function setTemplateStatus($data)
    {
        $template_id             = getProp($data, 'template_id');
        $distribution_channel_id = Site::getCurrentChannelId();
        $template                = DB::table('channel_templates')->where('id', $template_id)->where('distribution_channel_id', $distribution_channel_id)->first();
        if (!$template) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        $is_enable = getProp($data, 'is_enable');
        if ($is_enable === '' || !in_array($is_enable, [0, 1])) {
            Utils::throwError('1002:未知状态');
        }

        if ((int)$is_enable === 0 && (int)getProp($template, 'template_type') === 1) {
            Utils::throwError('1002:默认模版不可被停用');
        }

        return DB::table('channel_templates')->where('id', $template_id)->update([
            'is_enable'  => $is_enable,
            'updated_at' => date('Y-m-d H:i:s')
        ]);
    }

    /**
     * 新增模版
     *
     * @param $data
     * @return bool
     * @throws ApiException
     */
    public function addTemplate($data)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $user_scope              = getProp($data, 'user_scope');
        if ((int)$user_scope === 1) {
            Utils::throwError('1002:不可选择该适用人群');
        }
        $template_arr = [
            'distribution_channel_id' => $distribution_channel_id,
            'template_name'           => getProp($data, 'template_name'),
            'template_type'           => 2,
            'user_scope'              => $user_scope,
            'orders'                  => $user_scope == 3 ? 0 : getProp($data, 'orders', 0),
            'is_enable'               => getProp($data, 'is_enable', 0),
            'template_cate'           => getProp($data, 'template_cate', 1),
            'created_at'              => date('Y-m-d H:i:s'),
            'updated_at'              => date('Y-m-d H:i:s'),
        ];

        $channel_type = DB::table('distribution_channels')->where('id', $distribution_channel_id)->value('channel_type');

        if (DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('template_name', getProp($data, 'template_name'))->value('id')) {
            Utils::throwError('1002:模板名称重复,请更换');
        }

        try {
            DB::beginTransaction();
            $template_id = DB::table('channel_templates')->insertGetId($template_arr);
            if (!$template_id) {
                DB::rollBack();
                Log::info('创建模版失败: ' . json_encode($template_arr, 256));
                Utils::throwError('1002:创建模版失败');
            }

            $products = getProp($data, 'products', []);
            if (is_string($products)) {
                $products = json_decode($products, true);
            }

            $product_type  = BaseConst::PRODUCT_TYPE;
            $type_valid    = array_keys($product_type);
            $type_valid[]  = 'RECHARGE';
            $sequence      = 1;
            $product_arr   = [];
            $default_count = 0;
            foreach ($products as $product) {
                $type  = getProp($product, 'type');
                $price = sprintf('%.2f', getProp($product, 'price'));

                if (!$type || (!in_array($type, $type_valid)) || $price <= 0) {
                    Utils::throwError('1002:档位类型或价格设置有误,请重新设置!');
                }
                $given           = getProp($product, 'given', 0);
                $angle_sign_text = getProp($product, 'angle_sign_text');
                $price_desc      = '';
                $is_default      = getProp($product, 'is_default', 0);      // 是否默认
                if ($is_default) $default_count += 1;
                switch ($type) {
                    case 'RECHARGE':
                        if ($channel_type == 'PERIOD') Utils::throwError('1002:会员制站点不得设置充值档位,只能设置包周期档位!');
                        $name      = $price . '元';
                        $name_desc = '';
                        if ($given) $price_desc = '赠' . $given . '书币';
                        break;
                    default:
                        $name      = $product_type[$type];
                        $name_desc = $name;
                        break;
                }


                $product_arr[] = [
                    'type'            => $type,
                    'name'            => $name,
                    'name_desc'       => $name_desc,
                    'price'           => $price,
                    'price_desc'      => $price_desc,
                    'given'           => $given,
                    'angle_sign_text' => $angle_sign_text,
                    'is_default'      => $is_default,
                    'is_enabled'      => 1,
                    'sequence'        => $sequence,
                    'template_type'   => $template_id,
                    'template_id'     => $template_id,
                    'created_at'      => date('Y-m-d H:i:s'),
                    'updated_at'      => date('Y-m-d H:i:s'),
                ];
                $sequence      += 1;
            }
            if (count($product_arr) < 3 || count($product_arr) > 6) {
                Utils::throwError('1002:充值档位不得低于3个,也不得高于6个');
            }
            if ((int)$default_count === 0) {
                Utils::throwError('1002:请设置默认档位');
            }
            if ((int)$default_count > 1) {
                Utils::throwError('1002:只可设置一个默认档位');
            }

            $boolen = DB::table('products')->insert($product_arr);
            if (!$boolen) {
                DB::rollBack();
                Log::info('创建充值档位失败: ' . json_encode($product_arr, 256));
                Utils::throwError('1002:创建充值档位失败');
            }

        } catch (\Exception $e) {
            DB::rollback();
            Utils::throwError('1002:' . $e->getMessage());
        }
        DB::commit();
        return true;
    }

    /**
     * 编辑模版
     *
     * @param $data
     * @return bool
     * @throws ApiException
     */
    public function editTemplate($data)
    {
        $distribution_channel_id = Site::getCurrentChannelId();
        $user_scope              = getProp($data, 'user_scope');
        if ((int)$user_scope === 1) {
            Utils::throwError('1002:不可选择该适用人群');
        }
        $template_id = getProp($data, 'template_id');
        $template    = DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('id', $template_id)->first();
        if (!$template) {
            Utils::throwError('1002:该模版不存在');
        }
        $channel_type = DB::table('distribution_channels')->where('id', $distribution_channel_id)->value('channel_type');

        $id = DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('template_name', getProp($data, 'template_name'))->value('id');
        if ($id && $id != getProp($template, 'id')) {
            Utils::throwError('1002:模板名称重复,请更换');
        }

        $template_arr = [
            'distribution_channel_id' => $distribution_channel_id,
            'template_name'           => getProp($data, 'template_name'),
            'orders'                  => $user_scope == 3 ? 0 : getProp($data, 'orders', 0),
            'is_enable'               => getProp($data, 'is_enable', 0),
            'template_cate'           => getProp($data, 'template_cate', 1),
            'updated_at'              => date('Y-m-d H:i:s'),
        ];
        if (getProp($template, 'template_type') != 1) {
            $template_arr['user_scope'] = $user_scope;
        }

        $product_ids = DB::table('products')->where('template_id', $template_id)->select('id')->get()->pluck('id')->toArray();

        try {
            DB::beginTransaction();
            $boolen = DB::table('channel_templates')->where('id', $template_id)->update($template_arr);
            if (!$boolen) {
                DB::rollBack();
                Log::info('更新模版失败: ' . json_encode($template_arr, 256));
                Utils::throwError('1002:更新模版失败');
            }

            $products = getProp($data, 'products', []);
            if (is_string($products)) {
                $products = json_decode($products, true);
            }

            $product_type       = BaseConst::PRODUCT_TYPE;
            $type_valid         = array_keys($product_type);
            $type_valid[]       = 'RECHARGE';
            $sequence           = 1;
            $product_arr        = [];
            $default_count      = 0;
            $remain_product_ids = [];
            foreach ($products as $product) {
                $product_id = getProp($data, 'product_id');
                if ($product_id) $remain_product_ids[] = $product_id;
                $type  = getProp($product, 'type');
                $price = sprintf('%.2f', getProp($product, 'price'));

                if (!$type || (!in_array($type, $type_valid)) || $price <= 0) {
                    Utils::throwError('1002:档位类型或价格设置有误,请重新设置!');
                }
                $given           = getProp($product, 'given', 0);
                $angle_sign_text = getProp($product, 'angle_sign_text');
                $price_desc      = '';
                $is_default      = getProp($product, 'is_default', 0);      // 是否默认
                if ($is_default) $default_count += 1;
                switch ($type) {
                    case 'RECHARGE':
                        if ($channel_type == 'PERIOD') Utils::throwError('1002:会员制站点不得设置充值档位,只能设置包周期档位!');
                        $name      = $price . '元';
                        $name_desc = '';
                        if ($given) $price_desc = '赠' . $given . '书币';
                        break;
                    default:
                        $name      = $product_type[$type];
                        $name_desc = $name;
                        break;
                }
                $tmp = [
                    'type'            => $type,
                    'name'            => $name,
                    'name_desc'       => $name_desc,
                    'price'           => $price,
                    'price_desc'      => $price_desc,
                    'given'           => $given,
                    'angle_sign_text' => $angle_sign_text,
                    'is_default'      => $is_default,
                    'is_enabled'      => 1,
                    'sequence'        => $sequence,
                    'template_type'   => $template_id,
                    'template_id'     => $template_id,
                    'updated_at'      => date('Y-m-d H:i:s'),
                ];

                if ($product_id) {      // 更新已有的档位
                    $boolen1 = DB::table('product')->where('id', $product_id)->update($tmp);
                    if (!$boolen1) {
                        DB::rollBack();
                        Log::info('更新充值档位失败: ' . json_encode($tmp, 256));
                        Utils::throwError('1002:更新充值档位失败');
                    }
                } else {     // 写入新的档位
                    $tmp['created_at'] = date('Y-m-d H:i:s');
                    $product_arr[]     = $tmp;
                }

                $sequence += 1;
            }
            if (count($products) < 3 || count($products) > 6) {
                Utils::throwError('1002:充值档位不得低于3个,也不得高于6个');
            }
            if ((int)$default_count === 0) {
                Utils::throwError('1002:请设置默认档位');
            }
            if ((int)$default_count > 1) {
                Utils::throwError('1002:只可设置一个默认档位');
            }

            // 执行写入
            $boolen2 = DB::table('products')->insert($product_arr);
            if (!$boolen2) {
                DB::rollBack();
                Log::info('创建充值档位失败: ' . json_encode($product_arr, 256));
                Utils::throwError('1002:创建充值档位失败');
            }

            // 执行删除档位
            $delete_ids = array_diff($product_ids, $remain_product_ids);
            $boolen3    = DB::table('products')->whereIn('id', $delete_ids)->delete();
            if (!$boolen3) {
                DB::rollBack();
                Log::info('删除充值档位失败: ' . json_encode($delete_ids, 256));
                Utils::throwError('1002:删除充值档位失败');
            }

        } catch (\Exception $e) {
            DB::rollback();
            Utils::throwError('1002:' . $e->getMessage());
        }
        DB::commit();
        return true;
    }

    // 推广数据
    public function getPromotionData($data)
    {
        $promotionid = getProp($data, 'promotion_id');
        $start_date  = getProp($data, 'start_date');
        $end_date    = getProp($data, 'end_date');
        if (!$promotionid) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        $channel_id = DB::table('dy_report_logs')->leftJoin('send_orders', 'send_orders.id', 'dy_report_logs.send_order_id')->where('dy_report_logs.promotionid', $promotionid)->value('send_orders.distribution_channel_id');

        $query = DB::table('dy_report_logs')->leftJoin('orders', 'orders.uid', 'dy_report_logs.uid')->where('orders.status', 'PAID')
                   ->where('dy_report_logs.promotionid', $promotionid)->select('dy_report_logs.uid')->groupBY('dy_report_logs.uid');

        $uids = $query->get()->pluck('uid')->toArray();

        $result = [
            'distribution_channel_id' => $channel_id,
            'promotion_id'            => $promotionid,
            'recharge_amount'         => 0,
            'first_recharge_amount'   => 0,
            'recharge_user_num'       => count($uids),
            'recharge_order_num'      => 0,
        ];

        $where = [];
        if ($start_date) {
            $start   = date('Y-m-d 00:00:00', strtotime($start_date));
            $where[] = [
                'created_at', '>=', $start
            ];
        }

        if ($end_date) {
            $end     = date('Y-m-d 23:59:59', strtotime($end_date));
            $where[] = [
                'created_at', '<=', $end
            ];
        }

        if (!$start_date && !$end_date) {
            $start = date('Y-m-d 00:00:00');
            $end   = date('Y-m-d 23:59:59');
            $where = [
                [
                    'created_at', '>=', $start
                ],
                [
                    'created_at', '<=', $end
                ]
            ];
        }

        // 获取充值总额
        $result['recharge_amount']       = DB::table('orders')->whereIn('uid', $uids)->where('status', 'PAID')->where($where)->sum('price');
        $result['recharge_order_num']    = DB::table('orders')->whereIn('uid', $uids)->where('status', 'PAID')->where($where)->count('id');
        $result['first_recharge_amount'] = DB::table('orders')->whereIn('uid', $uids)->where('status', 'PAID')->where($where)->where('pay_num', 1)->sum('price');

        return $result;
    }

    // 补回传
    public function reportPromotionOrder($data)
    {
        $report_id = getProp($data, 'report_id');
        if (!$report_id) {
            Utils::throwError(ErrorConst::DATA_EXCEPTION);
        }

        $dy_report_log = DB::table('dy_report_logs')->where('id', $report_id)->where('callback_result', '失败')->where('remark', '比例过滤')->first();
        if (!$dy_report_log) {
            Utils::throwError('1002:暂无可回传数据');
        }

        $clickid   = getProp($dy_report_log, 'clickid');
        $post_data = [
            'clickid'    => $clickid,
            'event_type' => 'active_pay',
            'context'    => [
                'ad' => [
                    'callback' => $clickid,
                ]
            ],
            'timestamp'  => (int)(microtime(true) * 1000),
        ];
        try {
            $client        = new Client(['verify' => false]);
            $response      = $client->post('https://analytics.oceanengine.com/api/v2/conversion', ['json' => $post_data]);
            $response_json = $response->getBody()->getContents();
            $result        = json_decode($response_json, true);

            $update_data = [
                'callback_result'   => (isset($result['message']) && $result['message'] == '成功') ? '成功' : '失败',
                'callback_response' => $response_json,
                'remark'            => isset($result['message']) ? ($result['message'] == '成功' ? '手动回传' : $result['message']) : '回参错误',
                'updated_at'        => date('Y-m-d H:i:s')
            ];

            // 写入抖音广告主投放回调日志表
            $boolen = DB::table('dy_report_logs')->where('id', getProp($dy_report_log, 'id'))->update($update_data);
            if (!$boolen) {
                dLog('reportDy')->info('写入抖音广告主投放回调日志表失败: ', $update_data);
                Utils::throwError(ErrorConst::REPORT_FAILED);
            }
            return $boolen;
        } catch (\Exception $e) {
            dLog('reportDy')->info('上报异常: ', ['error' => $e->getMessage()]);
        }
    }
}
