<?php

namespace app\repository;

use app\exception\RepositoryException;
use app\model\Account;
use app\model\AccountAddress;
use app\model\AccountCoupon;
use app\model\AccountDataLog;
use app\model\Express;
use app\model\Order;
use app\model\OrderActivity;
use app\model\OrderAfterSale;
use app\model\OrderGroupList;
use app\model\OrderSku;
use app\model\PaymentLog;
use app\model\ShoppingCart;
use app\model\Sku;
use app\model\Spu;
use app\model\SpuActivity;
use app\service\Kd100;
use app\service\Math;
use app\service\Repository;
use app\service\wx\WechatPay;
use app\traits\order\ActivityTrait;
use app\traits\order\AfterSaleTrait;
use app\traits\order\ExpressLogTrait;
use app\traits\order\ExpressTrait;
use app\traits\order\ShoppingCartTrait;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Config as CConfig;
use think\facade\Db;
use think\facade\Log;
use think\Model;
use function EasyWeChat\Kernel\Support\generate_sign;

/**
 * 订单域 相关操作
 *
 * Class OrderRepository
 * @package app\repository
 * @method self getInstance(Model $model = null) static
 */
class OrderRepository extends Repository
{
    use ShoppingCartTrait;
    use AfterSaleTrait;
    use ExpressTrait;
    use ExpressLogTrait;
    use ActivityTrait;

    /** 订单状态 **/
    public const STATUS_ORDER_PLACED        = Order::STATUS_ORDER_PLACED;//已下单  (已付款待发货)
    public const STATUS_MAKEING             = Order::STATUS_MAKEING;//制作中
    public const STATUS_SHIPPED             = Order::STATUS_SHIPPED;//已发货
    public const STATUS_ARRIVED             = Order::STATUS_ARRIVED;//已送达
    public const STATUS_COMPLETED           = Order::STATUS_COMPLETED;//已完成 确认收获 或者 到期自动确认
    public const STATUS_CANCEL              = Order::STATUS_CANCEL;//已取消

    public const IS_EVALUATE_YES = 1; // is_evaluate 已评价(订单中的已评价指全部评价完)
    public const IS_EVALUATE_NO  = 0; // is_evaluate 未评价(订单中的已评价指全部评价完)

    /** 订单状态文本描述 **/
    public static function orderStatusTextList(): array
    {
        return [
            self::STATUS_ORDER_PLACED   => '已下单',
            self::STATUS_MAKEING        => '制作中',
            self::STATUS_SHIPPED        => '已发货',
            self::STATUS_ARRIVED        => '已送达',
            self::STATUS_COMPLETED      => '已完成',
            self::STATUS_CANCEL         => '已取消',
        ];
    }

    /**
     * 获取订单列表
     *
     * @param  int  $accountId
     * @param  array  $fields
     * @return array
     * @throws RepositoryException
     */
    public function listByUid(int $accountId, array $fields = []): array
    {
        return $this->findList(['account_id' => $accountId], $fields);
    }

    /**
     * 获取指定sku库存
     *
     * @param  array  $codingIds
     * @return array
     */
    public function stock(array $codingIds): array
    {
        return Sku::where('coding', 'in', $codingIds)->column('stock', 'coding');
    }

    /**
     * 订单提交数据 校验+处理
     *
     * @param  array  $data
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function validateOrderData(array $data): array
    {
        /** 订单提交的数据 必需按下列格式
         * $data['sku_list']    = [
         * [
         * 'sku_coding' => '16261684919319972261',
         * 'group_id' => 1,
         * 'num'        => 1,
         * ],
         * [
         * 'sku_coding' => '16261684169515092561',
         * 'group_id' => 0,
         * 'num'        => 1,
         * ],
         * [
         * 'sku_coding' => '16261684169515092561',
         * 'group_id' => 1,
         * 'num'        => 1,
         * ],
         * ];
         * $data['total']       = 950; //订单应付总金额(前端计算的结果)单位:元
         * $data['address_id']  = 26; //地址ID 0=自提
         * $data['express_code']  = ''; //快递公司代号
         * $data['pick_self']  = 0; //是否自提 0=否 1=是
         * $data['pick_self_phone']  = '13541194066'; //自提时必填 自提人联系方式
         * $data['original_total']  = 1000; //商品总价 单位元
         * $data['freight']  = 0; //运费 单位元
         * $data['remarks'] = '';//买家备注
         */

        if (!isset($data['sku_list']) || empty($data['sku_list'])) {
            throw new RepositoryException('商品不能为空');
        }

        if (!isset($data['address']) || empty($data['address'])) {
            throw new RepositoryException('收货地址不能为空');
        }

        if (!isset($data['phone']) || empty($data['phone'])||!preg_match("/^1[3456789]{1}\d{9}$/",$data['phone'])) {
            throw new RepositoryException('收货联系电话不能为空或格式不正确');
        }
        if (!isset($data['contacts']) || empty($data['contacts'])) {
            throw new RepositoryException('收货联系人不能为空');
        }

//        if (!isset($data['pick_self']) ||
//            !isset($data['freight'])) {
//            throw new RepositoryException('订单参数错误');
//        }

//        $data['coupon_id']    = $data['coupon_id'] ?? 0;
//        $data['coupon_price'] = $data['coupon_price'] ?? 0;
//        $data['is_score']     = $data['is_score'] ?? 0;

//        if ($data['pick_self'] == Order::COMMON_OFF && !isset($data['address_id'])) {
//            throw new RepositoryException('请选择收货地址');
//        }

//        if ($data['pick_self'] == Order::COMMON_ON && !isset($data['pick_self_phone'])) {
//            throw new RepositoryException('自提时请填写联系人方式');
//        }

//        if (isset($data['express_code']) && !empty($data['express_code'])) {
//            if (!$express = Express::where('code', $data['express_code'])->find()) {
//                throw new RepositoryException('快递公司不存在');
//            }
//            $data['express_name'] = $express['name'] ?? '';
//        }

        $skuList   = $this->handleSku($data['sku_list']);
        //$groupList = $this->handleGroup($data['sku_list']);

        $data['sku_list']   = $skuList;
        //$data['group_list'] = $groupList;

        return $data;
    }

    /**
     * sku数据处理
     *
     * @param  array  $data
     * @return array
     * @throws RepositoryException
     */
    protected function handleSku(array $data): array
    {
        $skuList = [];
        foreach ($data as $sku) {
            if (!isset($sku['sku_coding']) || empty($sku['sku_coding'])) {
                throw new RepositoryException('SKU编码不能为空');
            }

            //多个相同sku_coding合并
            if (!empty($skuList[$sku['sku_coding']])) {
                $skuList[$sku['sku_coding']] += $sku['num'] ?? 1;
            } else {
                $skuList[$sku['sku_coding']] = $sku['num'] ?? 1;
            }
        }

        return $skuList;
    }

    /**
     * 拼团数据处理
     *
     * @param  array  $data
     * @return array
     * @throws RepositoryException
     */
    protected function handleGroup(array $data): array
    {
        $list = [];
        foreach ($data as $sku) {
            if (!isset($sku['sku_coding']) || empty($sku['sku_coding'])) {
                throw new RepositoryException('SKU编码不能为空');
            }

            $list[$sku['sku_coding']] = $sku['group_id'] ?? 0;
        }

        return $list;
    }

    /**
     * 订单前置数据 校验+处理
     *
     * @param  array  $data
     * @return array
     * @throws RepositoryException
     */
    public function validatePrepareData(array $data): array
    {
        /** 订单前置数据 必需按下列格式
         * $data['sku_list']    = [
         * [
         * 'sku_coding' => '16261684919319972261',
         * 'num'        => 1,
         * ],
         * [
         * 'sku_coding' => '16261684169515092561',
         * 'num'        => 1,
         * ],
         * [
         * 'sku_coding' => '16261684169515092561',
         * 'num'        => 1,
         * ],
         * ];
         */

        if (!isset($data['sku_list']) || empty($data['sku_list'])) {
            throw new RepositoryException('商品不能为空1');
        }

        $skuList = $this->handleSku($data['sku_list']);

        $data['sku_list'] = $skuList;

        return $data;
    }

    /**
     * 创建订单 流程:1、参数完整性校验 2、参数正确性校验[收货地址、商品列表、商品价格等] 3、对比前端计算结果 4、创建订单
     *
     * @param  int  $accountId  用户ID
     * @param  array  $data  订单数据
     * @return Model
     * @throws RepositoryException|GuzzleException
     */
    public function createOrder(int $accountId, array $data): Model
    {
        // 启动事务
        Db::startTrans();
        try {
            $account = Account::where('id', $accountId)->lock(true)->find();
            if (!$account) {
                throw new RepositoryException('用户不存在');
            }

            $data      = $this->validateOrderData($data);
            $dataSku   = $data['sku_list'];//商品sku coding=>num 商品数量

            $skuList = SpuRepository::getInstance()->listBySkuCoding(array_keys($dataSku), true);
            $skuPriceList = $skuList->column('original_price', 'coding');
//            if (isset($data['is_only']) && $data['is_only'] > Spu::COMMON_OFF) {
//                $skuPriceList = $skuList->column('original_price', 'coding');
//            } else {
//                $skuPriceList = $skuList->column('price', 'coding');
//            }
            $skuScoreList = $skuList->column('score', 'coding');//商品规格=》积分
            $skuStockList = $skuList->column('stock', 'coding');
            $totalPrice   = 0;//商品原总价 单位(元) 未做任何优惠
            $totalScore   = 0;//商品总积分
            foreach ($dataSku as $coding => $num) {
                if (!isset($skuPriceList[$coding])) {
                    throw new RepositoryException('部分商品不存在或已下架 规格编码:'.$coding);
                }
                $totalPrice += $skuPriceList[$coding] * $num;
                $totalScore += $skuScoreList[$coding] * $num;
                if ($skuStockList[$coding] < $num) {
                    throw new RepositoryException('有商品库存不足');
                }
            }

            $skuArr = $skuList->each(function ($item) use ($dataSku, $accountId) {
                $item->num        = $dataSku[$item->coding] ?? 1;
                $item->account_id = $accountId;
            });

            // 限购检查 包含活动商品和普通商品
            $this->skuCheck($skuArr->toArray());

            //商品减库存
            $updateSkuStock = [];//规格库存
            foreach ($skuList as $sku) {
                $arr          = [];
                $arr['id']    = $sku['id'];
                $arr['stock'] = Db::raw('`stock` - '.($dataSku[$sku['coding']] ?? 1));;
                $updateSkuStock[] = $arr;
            }

            (new Sku())->saveAll($updateSkuStock);

           // $freight = 0;//运费(元)
//            // 邮寄方式
//            if ($data['pick_self'] == Order::COMMON_OFF) {
//                // 快递公司默认运费
//                if (isset($data['express_code']) && !empty($data['express_code'])) {
//                    $freight = Express::getDefaultFreight($data['express_code']);
//                }
//
//                if (!$address = AccountAddress::findById($data['address_id'])) {
//                    throw new RepositoryException('收货地址不存在');
//                }
//
//                $addressInfo = sprintf("%s,%s,%s %s %s %s", $address['name'], $address['phone'],
//                    $address['province_str'], $address['city_str'], $address['county_str'], $address['address']);
//            } else {
//                $addressInfo = '自提';
//            }

            //$realTotalPrice = $totalPrice - $data['coupon_price'];//实际应支付=商品总价-优惠券金额
            $realTotalPrice = $totalPrice;//实际应支付=商品总价-优惠券金额
            $realTotalPrice = $realTotalPrice <= 0 ? 0 : $realTotalPrice;

           // $realTotalPrice += $freight;//加运费

            $insert = [];
            $time   = time();
            $now    = date('Y-m-d H:i:s', $time);
            // 付款有效期
            $expired = date('Y-m-d H:i:s', $time + 15 * 60);

            $payType = Order::PAY_TYPE_WECHAT;

            // 积分支付
//            if ($data['is_score'] > 0) {
//                if ($account['score'] < $totalScore) {
//                    throw new RepositoryException('积分不足');
//                }
//                $payType = Order::PAY_TYPE_SCORE;
//            }

            $create = [
                'coding'         => generateCode(),
                'account_id'     => $accountId,
                'original_price' => $totalPrice,
                'price'          => $realTotalPrice,
                'pay_type'       => $payType,//支付方式
                'status'         => self::STATUS_ORDER_PLACED,
                'created_at'     => $now,
                'score'          => $totalScore,//使用积分
                'expired_at'     => $expired,
                'address_id'     => $data['address_id'] ?? 0,
                //'address'        => $addressInfo,
                'remarks'        => $data['remarks'] ?? '',
                'express_code'   => $data['express_code']??'',
                'express_name'   => $data['express_name'] ?? '',
                'pick_self'      => $data['pick_self'] ??0,
                'coupon_id'      => $data['coupon_id'] ?? 0,
                'freight'        => 0,
                'coupon_price'   => $data['coupon_price'] ?? 0,
                //'phone'          => $address['phone'] ?? $data['pick_self_phone'],
                'is_only'        => isset($data['is_only']) && $data['is_only'] > 0 ? 1 : 0,
                'is_score'       => $data['is_score'] ?? 0,

                'wedding_date'                  => $data['wedding_date'] ?? null,
                'expected_delivery_date'        => $data['expected_delivery_date'] ?? null,
                'address'                       => $data['address'] ?? '',
                'phone'                         => $data['phone'] ?? '',
                'contacts'                      => $data['contacts'] ?? '',
            ];

            if ($realTotalPrice == 0) {
                $create['status']  = self::STATUS_ORDER_PLACED;
                $create['paid_at'] = $now;
            }

            //创建订单
            if (!$order = $this->create($create)) {
                throw new RepositoryException('订单创建失败,请稍后重试');
            }

//            // 优惠券状态变更
//            if ($data['coupon_id'] > 0 && $data['coupon_price'] > 0) {
//                AccountCoupon::use($accountId, $data['coupon_id'], $order['coding']);
//            }

            // 创建活动商品订单
            //            OrderRepository::getInstance()->createActivityOrder($accountId, $order->coding, $skuArr->toArray());

//            if ($realTotalPrice > 0) {
//                //创建支付记录
//                PaymentLog::create([
//                    'account_id'   => $accountId,
//                    'order_coding' => $order['coding'],
//                    'created_at'   => $now,
//                    'type'         => 'order',
//                    'amount'       => $order['price'],
//                ]);
//            }

//            $order->needPay  = $realTotalPrice > 0;//是否需要付款
            $orderHasVirtual = 0;//是否拥有虚拟商品

            $skuIds = [];//当前订单涉及到的skuID
            foreach ($skuList as $sku) {
                $skuIds[] = $sku->id;

                $arr                    = [];
                $arr['order_coding']    = $order->coding;
                $arr['coding']          = $sku->coding;
                $arr['spu_id']          = $sku->spu_id;
                $arr['spu_activity_id'] = $sku->spu_activity_id;
                $arr['sku_id']          = $sku->id;
                $arr['account_id']      = $accountId;
                $arr['price']           = (isset($data['is_only']) && $data['is_only'] > Spu::COMMON_OFF) ? $sku->original_price : $sku->price;
                $arr['score']           = $sku->score;
                $arr['num']             = $dataSku[$sku->coding];
                $arr['status']          = 'normal';
                $arr['created_at']      = $now;
                $arr['spu_cover']       = $sku->spu_cover;
                $arr['spu_name']        = $sku->spu_name;
                $arr['sku_cover']       = $sku->sku_cover;
                $arr['sku_name']        = $sku->sku_name;
                $arr['is_virtual']      = $sku->is_virtual;
                $arr['check_type']      = $sku->check_type;
                $arr['spec_text']       = $sku->spec_text ? json_encode($sku->spec_text, JSON_UNESCAPED_UNICODE) : '';
                $arr['not_check_num']   = $dataSku[$sku->coding];
                $arr['has_comment']     = OrderSku::HAS_COMMENT_NO;
                $arr['is_activity']     = $sku->is_activity;
                $arr['activity_type']   = $sku->activity_type;
                $arr['sku_unit']        = $sku->unit;
                $arr['subtotal']        = $sku->original_price * $dataSku[$sku->coding];
                $insert[]               = $arr;

                if ($sku->is_virtual > 0) {
                    $orderHasVirtual = 1;
                }
            }

            if ($orderHasVirtual > 0) {
                $order->save(['has_virtual' => Order::COMMON_ON]);
            }

            // 删除购物车中相关SKU
            ShoppingCart::whereIn('sku_id', $skuIds)->where('account_id', $accountId)->delete();

            //保存订单商品
            if (!(new OrderSku())->saveAll($insert)) {
                throw new RepositoryException('订单商品保存失败');
            }

//            if ($realTotalPrice > 0) {
//                //统一下单
//                $res = $this->wechatMiniPay($order->coding, $account->openid, Math::yuan2Fen($realTotalPrice));
//                $order->save(['prepay_id' => $res['prepay_id']]);
//
//                //生成小程序支付请求的参数
//                $order->payment_params = $this->miniPayReqParams($res['prepay_id']);
//            }

            // 积分订单 扣减积分并记录
//            if ($totalScore > 0) {
//                AccountDataLog::log($accountId,
//                    '积分商品下单', -$totalScore,
//                    AccountDataLog::TYPE_SCORE,
//                    AccountDataLog::ACTION_ORDER,
//                    $account['score'] - $totalScore);
//                $account->score = Db::raw('`score` - '.$totalScore);
//            }
            $account->mobile    = $data["phone"];
            $account->address   = $data["address"];
            $account->contacts  = $data["contacts"];
            $account->save();

//            if ($realTotalPrice == 0) {
//                // 设为已付款
//                $this->afterPaid($accountId, $order['coding']);
//            }
            // 设为已付款
            $this->afterPaid($accountId, $order['coding']);

            Db::commit();
            return $order;
        } catch (RepositoryException $e) {
            Db::rollback();
            throw new RepositoryException($e->getMessage(), $e->getCode());
        } catch (Exception $e) {
            //回滚
            Db::rollback();
            self::log('订单创建失败', $e, 'error', 'order');
            throw new RepositoryException('订单创建失败'.$e->getMessage());
        }
    }

    /**
     * 根据统一下单返回的prepay_id 生成小程序支付请求参数
     *
     * @param  string  $prepayId
     * @return array
     */
    protected function miniPayReqParams(string $prepayId): array
    {
        CConfig::load('extra/wechat', 'wechat');
        $config         = config('wechat');
        $params         = [
            'appId'     => $config['applets_appId'] ?? '',
            'timeStamp' => (string) time(),
            'nonceStr'  => uniqid(),
            'package'   => 'prepay_id='.$prepayId,
            'signType'  => 'MD5'
        ];
        $params['sign'] = generate_sign($params, $config['key']);

        return $params;
    }

    /**
     * 微信支付
     *
     * @param  string  $orderId
     * @param  string  $openid
     * @param  int  $price
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|ResponseInterface|string
     * @throws GuzzleException
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws RepositoryException
     */
    public function wechatMiniPay(string $orderId, string $openid, int $price)
    {
        $res = WechatPay::getInstance()->order->unify([
            'body'         => '商品购买',
            'out_trade_no' => $orderId,
            'total_fee'    => $price,
            'trade_type'   => 'JSAPI',
            'openid'       => $openid,
        ]);

        $log = sprintf("[微信统一下单返回][订单号=%s]%s", $orderId, json_encode($res, JSON_UNESCAPED_UNICODE));
        Log::info($log);

        //return_code仅是通信状态 非支付状态
        if (isset($res['return_code']) && $res['return_code'] == 'SUCCESS') {
            if (isset($res['result_code']) && $res['result_code'] == 'SUCCESS') {
                return $res;
            } else {
                throw new RepositoryException('支付请求失败');
            }
        } else {
            throw new RepositoryException('请求微信接口失败');
        }
    }

    /**
     * 订单准备信息获取
     * 即根据商品(sku)列表 收货地址 返回商品列表信息、运费、总价、可用分红额度等信息
     *
     * @param  int  $accountId  用户ID
     * @param  array  $data  订单数据
     * @return array
     * @throws RepositoryException
     */
    public function prepareInfo(int $accountId, array $data): array
    {
        $data    = $this->validateprepareData($data);
        $dataSku = $data['sku_list'];//商品sku coding=>num 商品数量

        try {
            $res = [];//待返回数据
            if (!$account = Account::findById($accountId, ['id', 'score'])) {
                throw new RepositoryException('读取用户信息失败');
            }

            $skuList      = SpuRepository::getInstance()->listBySkuCoding(array_keys($dataSku));
            $skuPriceList = $skuList->column('price', 'coding');
            $totalPrice   = 0;//商品原总价 单位(元) 未做任何优惠
            foreach ($dataSku as $coding => $num) {
                $totalPrice += $skuPriceList[$coding] * $num;
            }

            $skuList->each(function ($item) use ($dataSku, $data) {
                $item->num = $dataSku[$item->coding] ?? 1;
                if (!empty($item->spu_cover) && !isHttpUrl($item->spu_cover)) {
                    $item->spu_cover = $data['domain'].$item->spu_cover;
                }
                if (!empty($item->sku_cover) && !isHttpUrl($item->sku_cover)) {
                    $item->sku_cover = $data['domain'].$item->sku_cover;
                }
            });

            $res['original_total'] = $totalPrice;
            $res['list']           = $skuList;
            $res['account']        = $account;
            $res['discount']       = 0;

            return $res;
        } catch (RepositoryException $e) {
            throw new RepositoryException($e->getMessage(), $e->getCode());
        } catch (Exception $e) {
            self::log('订单准备信息获取失败', $e, 'error', 'order');
            throw new RepositoryException('订单数据错误');
        }
    }


    /**
     * 获取可以自动收货的订单列表
     *
     * @param  int  $day
     * @return Collection
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function autoReceiptList(int $day = 20): Collection
    {
        //发货后day天 自动确认收货
        $shipped_at = date('Y-m-d H:i:s', time() - $day * 86400);
        return $this->model->where('status', self::STATUS_SHIPPED)
            ->field('status,id,coding')
            ->where('shipped_at', '<', $shipped_at)
            ->select();
    }

    /**
     * 支付过期的订单列表
     *
     * @return Collection
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function expiredList(): Collection
    {
        //过期时间大于当前时间
        return $this->model->where('status', self::STATUS_WAITING)
            ->field('status,id')
            ->where('expired_at', '<', date('Y-m-d H:i:s'))
            ->select();
    }

    /**
     * 订单状态改为已过期
     *
     * @param  string  $orderCoding
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function setExpired(string $orderCoding): bool
    {
        return $this->setClosed($orderCoding, Order::STATUS_EXPIRED);
    }

    /**
     * 检测用户所有已过期的订单
     *
     * @param  int  $accountId
     * @return bool
     */
    public function checkAccountExpiredOrders(int $accountId = 0): bool
    {
        if ($accountId <= 0) {
            return false;
        }

        Db::startTrans();
        try {
            if ($accountId > 0 && !Account::findById($accountId)) {
                throw new RepositoryException('用户不存在');
            }

            $where   = [];
            $where[] = ['status', '=', self::STATUS_WAITING];
            if ($accountId > 0) {
                $where[] = ['account_id', '=', $accountId];
            }

            $waitingOrders = $this->model->where($where)->select();

            $toExpiredIds      = [];
            $toExpiredOrderIds = [];
            $nowTime           = time();

            foreach ($waitingOrders as $order) {
                if (!empty($order['expired_at'])) {
                    $expireTime = strtotime($order['expired_at']);
                    if ($nowTime >= $expireTime) {
                        $toExpiredIds[]      = $order['id'];
                        $toExpiredOrderIds[] = $order['coding'];
                    }
                }
            }

            // 订单状态变更
            if (count($toExpiredIds) > 0) {
                $this->model->whereIn('id', $toExpiredIds)
                    ->update(['status' => OrderRepository::STATUS_EXPIRED]);
            }

            // 商品库存量恢复
            if (count($toExpiredOrderIds) > 0) {
                $this->backStock($toExpiredOrderIds);
            }

            Db::commit();
        } catch (RepositoryException | Exception $e) {
            Db::rollback();
            self::log('订单已过期检测失败', $e, 'error', 'order');
            return false;
        }

        return true;
    }

    /**
     * 退还订单库存
     *
     * @param  array  $orderCoding  订单编号数组
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function backStock(array $orderCoding)
    {
        $orderSkus = OrderSku::whereIn('coding', $orderCoding)->field('num, sku_id')->select();
        if (!$orderSkus->isEmpty()) {
            $sku2Num = [];
            foreach ($orderSkus as $item) {
                if (isset($sku2Num[$item['sku_id']])) {
                    $sku2Num[$item['sku_id']] += $item['num'];
                } else {
                    $sku2Num[$item['sku_id']] = $item['num'];
                }
            }

            $skuList = Sku::where('id', 'in', array_keys($sku2Num))->field('id,stock')->lock(true)->select();
            $skuList->each(function ($item) use ($sku2Num) {
                if (isset($sku2Num[$item->id])) {
                    $item->stock += $sku2Num[$item->id];
                }
            });
            $backSkuStocks = $skuList->toArray();
            (new Sku())->saveAll($backSkuStocks);
        }
    }

    /**
     * 订单状态改为已支付
     *
     * @param  string  $orderCoding
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function setPaid(string $orderCoding): bool
    {
        if (!$order = $this->findOneByWhere(['coding' => $orderCoding])) {
            throw new RepositoryException('订单不存在');
        }

        if ($order['status'] == OrderRepository::STATUS_PAID) {
            $this->afterPaid($order['account_id'], $orderCoding);

            return true;
        }

        if ($order['status'] != OrderRepository::STATUS_PAID && $order['status'] != OrderRepository::STATUS_WAITING) {
            throw new RepositoryException('订单状态异常,若您已支付,请联系管理员');
        }

        $now = date('Y-m-d H:i:s');
        if ($order['status'] === OrderRepository::STATUS_WAITING) {
            Db::startTrans();
            try {
                $payment = PaymentLog::findOne(['account_id' => $order['account_id'], 'coding' => $orderCoding]);
                if (!$payment) {
                    PaymentLog::create([
                        'account_id' => $order['account_id'],
                        'coding'     => $orderCoding,
                        'created_at' => $order['created_at'],
                        'paid_at'    => $now,
                        'type'       => 'order',
                        'amount'     => $order['price'],
                    ]);
                } else {
                    $payment->paid_at = $now;
                    $payment->status  = PaymentLog::COMMON_ON;
                    $payment->save();
                }

                $order->status = Order::STATUS_PAID;
                if ($order['pick_self'] == Order::COMMON_ON) {
                    //自提 改为已发货
                    $order->status     = Order::STATUS_SHIPPED;
                    $order->shipped_at = $now;
                }

                $order->paid_at = $now;
                $res            = $order->save();

                $this->afterPaid($order['account_id'], $orderCoding);

                if ($res) {
                    Db::commit();
                }
                return $res;
            } catch (RepositoryException $e) {
                Db::rollback();
                throw new RepositoryException($e->getMessage(), $e->getCode());
            } catch (Exception $e) {
                //回滚
                Db::rollback();
                self::log('支付成功状态变更失败', $e, 'error', 'order');
                throw new RepositoryException('操作失败');
            }
        }

        return false;
    }

    /**
     * 支付后调用
     *
     * @param  int  $accountId
     * @param  string  $orderCoding
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function afterPaid(int $accountId, string $orderCoding)
    {
        OrderSku::setPaid($orderCoding);//订单商品
        //OrderActivity::setPaid($orderCoding);//活动订单
       // OrderGroupList::setPaid($orderCoding);//订单拼团列表

        // 付款后 所有商品销量添加
        $this->updateAmount($orderCoding);

        //清理除购物车
        OrderRepository::getInstance()->delShoppingCartByOrder($accountId, $orderCoding);
    }

    /**
     * 取消支付后调用
     *
     * @param  int  $accountId
     * @param  string  $orderCoding
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function afterCancelPaid(int $accountId, string $orderCoding)
    {
        //        OrderSku::setPaid($orderCoding);//订单商品
        //        OrderActivity::setPaid($orderCoding);//活动订单
        //        OrderGroupList::setPaid($orderCoding);//订单拼团列表

        // 销量减少
        $this->updateAmount($orderCoding, 'reduce');
    }

    /**
     * 订单验收 - 确认收货
     *
     * @param  int  $orderId
     * @param  int  $accountId
     * @throws RepositoryException
     */
    public function orderAccepted(int $orderId, int $accountId = 0)
    {
        Db::startTrans();
        try {
            $order = OrderRepository::getInstance()->findById($orderId, []);
            if (empty($order)) {
                throw new RepositoryException('没有相关的订单记录');
            }

            $accountId = $accountId == 0 ? $order['account_id'] : $accountId;

            if (!in_array($order['status']  ,[self::STATUS_SHIPPED,self::STATUS_ARRIVED])) {
                throw new RepositoryException('当前订单状态不支持收货操作');
            }

            $accountRepo = AccountRepository::getInstance();
            $nowDateTime = date('Y-m-d H:i:s');

            $account = $accountRepo->findById($accountId, []);
            if ($order['account_id'] !== $accountId || empty($account)) {
                throw new RepositoryException('用户不存在');
            }

            $this->update([
                'status'          => self::STATUS_COMPLETED,
                'commission_give' => Order::COMMON_ON,
                'accepted_at'     => $nowDateTime
            ], ['id' => $orderId]);

            /*
            CConfig::load('extra/commission_withdrawal', 'commission');
            $config = config('commission');
             */
            // 一级佣金发放
            /*
           if ($account['inviter_account_id'] > 0 && isset($config['commission_first']) && $config['commission_first'] > 0) {
               $firstRate       = Math::div($config['commission_first'], 100);
               $firstCommission = Math::mul($order['price'], $firstRate);
               $accountFirst    = Account::findById($account['inviter_account_id']);
               if ($accountFirst && $firstCommission > 0) {
                   AccountDataLog::log($account['inviter_account_id'],
                       '订单确认收货-一级分销佣金发放',
                       $firstCommission,
                       AccountDataLog::TYPE_COMMISSION,
                       AccountDataLog::ACTION_ORDER,
                       Math::add($accountFirst['commission'], $firstCommission)
                   );
                   $accountFirst->save([
                       'commission' => Db::raw('`commission` + '.$firstCommission)
                   ]);
               }
           }
           */

            // 二级分销发放佣金
            /*
            if ($account['inviter_parent_id'] > 0 && isset($config['commission_second']) && $config['commission_second'] > 0) {
                $secondRate       = Math::div($config['commission_second'], 100);
                $secondCommission = Math::mul($order['price'], $secondRate);
                $accountSecond    = Account::findById($account['inviter_parent_id']);
                if ($accountSecond && $secondCommission > 0) {
                    AccountDataLog::log($account['inviter_parent_id'],
                        '订单确认收货-二级分销佣金发放',
                        $secondCommission,
                        AccountDataLog::TYPE_COMMISSION,
                        AccountDataLog::ACTION_ORDER,
                        Math::add($accountSecond['commission'], $secondCommission)
                    );
                    $accountSecond->save([
                        'commission' => Db::raw('`commission` + '.$secondCommission)
                    ]);
                }
            }
            */

            Db::commit();
        } catch (RepositoryException $e) {
            Db::rollback();

            throw new RepositoryException($e->getMessage());
        } catch (Exception $e) {
            Db::rollback();
            self::log('订单确认收货失败', $e, 'error', 'order');
            throw new RepositoryException('订单数据错误');
        }
    }

    /**
     * 我的订单
     *
     * @param  int  $accountId
     * @param  string  $tag  all=全部[待付款 已付款 待发货 已发货 已完成 已取消] after_sale=售后[已发货 已完成等]
     * @param  int  $page
     * @param  int  $size
     * @return array|null
     * @throws RepositoryException
     */
    public function mine(int $accountId, string $tag, int $page = 1, int $size = 20): ?array
    {
        $where   = [];
        $where[] = ['account_id', '=', $accountId];
        event('OrderCheck');


        switch ($tag) {
            //待付款 已付款 已过期 待发货 待收货
            case self::STATUS_ORDER_PLACED:
            case self::STATUS_MAKEING:
            case self::STATUS_SHIPPED:
            case self::STATUS_ARRIVED:
            case self::STATUS_COMPLETED:
            case self::STATUS_CANCEL:
                $status = [$tag];
                break;
            // 待评价 已确认收货 未评价
           /*
            case 'waiting_comment':
                $where[] = ['is_evaluate', '=', self::IS_EVALUATE_YES];
                $status  = [self::STATUS_COMPLETED];
                break;
           */
            // 待核验
            /*
            case 'check':
                $where[] = ['has_virtual', '=', Order::COMMON_ON];
                $where[] = ['virtual_check', '=', Order::COMMON_OFF];
                $where[] = ['frontend_check', '=', Order::COMMON_OFF];
                $status  = [self::STATUS_PAID, self::STATUS_COMPLETED, self::STATUS_SHIPPED];
                break;
            */
            //  售后记录
            /*
            case 'after_sale':
                $status  = [self::STATUS_PAID, self::STATUS_SHIPPED, self::STATUS_COMPLETED];
                $where[] = ['is_after_sale', '=', self::BOOL_TRUE];
                break;
            */
            default:
                $status = [
                    self::STATUS_ORDER_PLACED   ,
                    self::STATUS_MAKEING     ,
                    self::STATUS_SHIPPED       ,
                    self::STATUS_ARRIVED       ,
                    self::STATUS_COMPLETED     ,
                    self::STATUS_CANCEL        ,
                ];
        }

        if (!empty($status)) {
            $where[] = ['status', 'in', $status];
        }

        $fields = [];
        return $this->findList($where, $fields, $page, $size, function ($q) use ($tag) {
            return $q->with(['skus'])->order('created_at', 'desc');
        });
    }

    /**
     * 立即免拼 单人开团
     * 免拼逻辑 1. 直接将订单状态修改 2. 订单相关拼团列表的剩余数量 全部设为0
     * is_only=0 is_group_make=1 open_one=1 open_one_success=0,group_make_end_at < 当前时间 订单列表才显示【立即免拼】,其他都不显示
     *
     * @param  int  $accountId
     * @param  string  $coding
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function openOne(int $accountId, string $coding)
    {
        if (!$order = Order::findOne(['coding' => $coding])) {
            throw new RepositoryException('订单不存在');
        }

        if ($order['is_group_make'] != Order::COMMON_ON) {
            throw new RepositoryException('不是拼团订单');
        }

        if (in_array($order['status'], [Order::STATUS_WAITING, Order::STATUS_CLOSED, Order::STATUS_EXPIRED])) {
            throw new RepositoryException('订单状态错误');
        }

        if ($order['account_id'] != $accountId) {
            throw new RepositoryException('不是您的订单');
        }

        $order->save(['open_one_success' => Order::COMMON_ON]);
        OrderGroupList::where('coding', $coding)->save(['surplus' => 0]);
    }

    /**
     * 订单数量
     *
     * @param  int  $accountId
     * @return array
     */
    public function orderCount(int $accountId): array
    {
        $statusList = ['order_placed', 'makeing', 'shipped'];
        $data       = Order::where('account_id', $accountId)
            ->whereIn('status', $statusList)
            ->group('status')
            ->column('count(`id`) as count', 'status');
        $list       = [];
        foreach ($statusList as $status) {
            $list[$status] = $data[$status] ?? 0;
        }
        return $list;
    }

    /**
     * 订单详情
     *
     * @param  int  $orderId
     * @return mixed|null
     * @throws RepositoryException
     */
    public function detail(int $orderId)
    {
        $fields = [];
        return $this->findById($orderId, $fields, function ($q) {
            return $q->with(['skus'])->order('created_at', 'desc');
        });
    }

    /**
     * 获取订单物流信息
     *
     * @param  string  $orderCoding
     * @return array|Model
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function logistics(string $orderCoding)
    {
        $fields = ['id', 'status', 'coding', 'is_after_sale', 'prepay_id', 'original_price', 'price', 'express_number', 'express_code', 'express_name'];
        if (!$order = $this->model->with(['skus'])->field($fields)->where('coding', $orderCoding)->find()) {
            throw new RepositoryException('订单不存在');
        }

        $expressInfo = [];
        if (!empty($order['express_code']) && !empty($order['express_number'])) {
            try {
                if ($order['status'] == self::STATUS_COMPLETED) {
                    $exprLog = $this->findExpressLogByNu($order['coding'], $order['express_code'], $order['express_number']);
                    if (!empty($exprLog)) {
                        $expressInfo['status']     = 200;
                        $expressInfo['state']      = $exprLog['state'];
                        $expressInfo['state_desc'] = Kd100::state()[$exprLog['state']] ?? '';
                        $expressInfo['com']        = $exprLog['com'];
                        $expressInfo['nu']         = $exprLog['nu'];
                        $expressInfo['data']       = $exprLog['content'];
                    } else {
                        $expressInfo = $this->handleOrderExpressLog($order['coding'], $order['express_code'], $order['express_number']);
                    }
                } else {
                    $expressInfo = $this->handleOrderExpressLog($order['coding'], $order['express_code'], $order['express_number']);
                }
            } catch (Exception $e) {
                $expressInfo = [];
            }
        }

        $order->express_info = $expressInfo;

        return $order;
    }

    /**
     * 订单发货
     *
     * @param  int  $orderId  订单ID
     * @param  int  $expressId  快递公司ID
     * @param  string  $expressNumber  快递单号
     * @param  string  $businessRemarks  卖家备注
     * @throws RepositoryException
     */
    public function orderShipping(int $orderId, int $expressId, string $expressNumber = '', string $businessRemarks = '')
    {
        Db::startTrans();
        try {
            $order = $this->findById($orderId, []);
            if (empty($order)) {
                throw new RepositoryException('没有相关的订单记录');
            }

            if ($order['status'] !== self::STATUS_PAID) {
                throw new RepositoryException('当前订单状态不支持发货操作');
            }

            $express = $this->expressInfo($expressId);
            if (empty($express)) {
                throw new RepositoryException('相关的快递公司不存在');
            }

            if (empty($expressNumber) || strlen($expressNumber) > 100) {
                throw new RepositoryException('请填写正确的快递单号');
            }

            $this->update([
                'shipped_at'       => date('Y-m-d H:i:s'),
                'business_remarks' => $businessRemarks,
                'express_code'     => $express['code'],
                'express_name'     => $express['name'],
                'express_number'   => $expressNumber,
                'status'           => self::STATUS_SHIPPED,
            ], ['id' => $orderId]);

            Db::commit();
        } catch (RepositoryException $e) {
            Db::rollback();

            throw new RepositoryException($e->getMessage());
        } catch (Exception $e) {
            Db::rollback();
            self::log('订单发货失败', $e, 'error', 'order');
            throw new RepositoryException('订单发货失败');
        }
    }

    /**
     * 更新商品库存 TODO
     *
     * @param  array  $spuIds
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function updateSpuStock(array $spuIds)
    {
        $skuList   = Sku::whereIn('id', $spuIds)->field('id,spu_id,stock')->select()->toArray();
        $updateSpu = [];
        foreach ($skuList as $sku) {
            $arr = [];
            //            $arr['spu']
        }
    }

    /**
     * 商品规格检查 1.限购检查 2.活动时间检查 3.团购、拼团数量检查
     * 包含活动与普通商品
     * 商品数量不多的情况下,循环内部执行检测 TODO 待优化 部分查询可拿到循环外部
     * @param  array  $data
     * @return bool
     * @throws RepositoryException|Exception
     */
    public function skuCheck(array $data): bool
    {
        $now = date('Y-m-d H:i:s');
        foreach ($data as $item) {
            //限购检测
            if ($item['limit_num'] > 0) {
                //存在限购数量
                $where   = [];
                $where[] = ['is_paid', '=', OrderSku::COMMON_ON];

                if ($item['spu_id'] > 0) {
                    $where[] = ['spu_id', '=', $item['spu_id']];
                }

                if ($item['spu_activity_id'] > 0) {
                    $where[] = ['spu_activity_id', '=', $item['spu_activity_id']];
                }

                if ($item['limit_time'] > 0) {
                    $endAt   = date('Y-m-d 23:59:59');//结束时间
                    $day     = ($item['limit_time'] - 1) ?: 0;
                    $str     = '-'.$day.' days';
                    $beginAt = date('Y-m-d 00:00:00', strtotime($str));
                    $where[] = ['paid_at', '>', $beginAt];
                    $where[] = ['paid_at', '<', $endAt];
                    $where[] = ['account_id', '=', $item['account_id']];
                    //存在限购时间 数字单位为天
                    //结合限购数量 生效规则:N天内 限购数量-已购买数量 >= 本次购买数量 否则无法购买
                }

                $history = OrderSku::history($where, ['id', 'spu_name']);
                if ($item['limit_num'] - $history->count() < $item['num']) {
                    throw new RepositoryException(sprintf("商品[%s]%s仅限购买%d件", $item['goods_name'],
                            $item['limit_time'] > 0 ? $item['limit_time'].'天内' : '', $item['limit_num']).$item['coding']);
                }
            }

            // 活动时间检测
            if (!empty($item['begin_at'])) {
                if ($now < $item['begin_at']) {
                    throw new RepositoryException('商品['.$item['goods_name'].']商品尚未开始');
                }
            }
            // 活动时间检测
            if (!empty($item['end_at'])) {
                if ($now > $item['end_at']) {
                    throw new RepositoryException('商品['.$item['goods_name'].']商品已结束');
                }
            }
        }

        return true;
    }

    /**
     * 更新订单销量
     *
     * @param  string  $orderCoding
     * @param  string  $type  add=添加 reduce=减少
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws Exception
     */
    public function updateAmount(string $orderCoding, string $type = 'add'): bool
    {
        $op = '+';
        if ($type != 'add') {
            $op = '-';
        }
        $list = OrderSku::where('coding', $orderCoding)->select();
        if ($list->isEmpty()) {
            return true;
        }

        $spuList      = $list->where('spu_id', '>', 0)->toArray();
        $activityList = $list->where('spu_activity_id', '>', 0)->toArray();

        $spuUpdate = [];
        foreach ($spuList as $spu) {
            if (!isset($spuUpdate[$spu['spu_id']])) {
                $spuUpdate[$spu['spu_id']]           = [];
                $spuUpdate[$spu['spu_id']]['id']     = $spu['spu_id'];
                $spuUpdate[$spu['spu_id']]['amount'] = $spu['num'];
            } else {
                $spuUpdate[$spu['spu_id']]['amount'] += $spu['num'];
            }
        }

        foreach ($spuUpdate as &$spu) {
            $spu['amount'] = Db::raw('`amount` '.$op.' '.$spu['amount']);
        }

        $activityUpdate = [];
        foreach ($activityList as $activity) {
            if (!isset($spuUpdate[$activity['spu_activity_id']])) {
                $activityUpdate[$activity['spu_activity_id']]           = [];
                $activityUpdate[$activity['spu_activity_id']]['id']     = $activity['spu_activity_id'];
                $activityUpdate[$activity['spu_activity_id']]['amount'] = $activity['num'];
            } else {
                $activityUpdate[$activity['spu_activity_id']]['amount'] += $activity['num'];
            }
        }

        foreach ($activityUpdate as &$activity) {
            $activity['amount'] = Db::raw('`amount` '.$op.' '.$activity['amount']);
        }

        if (!empty($spuUpdate)) {
            (new Spu())->saveAll($spuUpdate);
        }

        if (!empty($activityUpdate)) {
            (new SpuActivity())->saveAll($activityUpdate);
        }

        return true;
    }

    /**
     * 取消订单或设置过期
     *
     * @param  string  $orderCoding
     * @param  string  $status  操作类型 默认为取消
     * @param  string  $reason  取消原因
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function setClosed(string $orderCoding, string $status = Order::STATUS_CANCEL, string $reason = ''): bool
    {
        if (!$order = Order::findOne(['coding' => $orderCoding])) {
            throw new RepositoryException('订单不存在');
        }

        if (!in_array($order['status'], [Order::STATUS_ORDER_PLACED])) {
            throw new RepositoryException('订单状态不允许此操作');
        }

        // 启动事务
        Db::startTrans();
        try {
            //库存还原
            $this->backStock([$orderCoding]);

            // 取消拼团 拼团数量未恢复,因拼团是绝对成功。即便此拼团名额满 也可以自行发起
            //            OrderGroupList::cancel($orderCoding);
            // 活动订单取消
            OrderActivity::cancel($orderCoding);

            //$account             = Account::findById($order['account_id']);
            $order->close_reason = $reason;
            //待付款订单
           // if ($order['status'] == Order::STATUS_ORDER_PLACED) {
                $order->status = $status;
                $order->save();
                //积分日志
                /*
                if ($order['score'] > 0) {
                    AccountDataLog::log($order['account_id'], '取消订单退回', $order['score'], AccountDataLog::TYPE_SCORE,
                        AccountDataLog::ACTION_ORDER, $account['score'] + $order['score']);
                }
                */
                //待付款时 积分才退回
                /*
                $account->save([
                    'score' => Db::raw('`score` + '.$order['score'])
                ]);
                */

                // 优惠券退回
                /*
                AccountCoupon::where('order_coding', $orderCoding)->where('account_id', $order['account_id'])
                    ->where('coupon_id', $order['coupon_id'])
                    ->where('status', AccountCoupon::STATUS_USED)
                    ->update(['status' => AccountCoupon::STATUS_NORMAL]);
                */
                Db::commit();
                return true;
            //}
            /*
            if ($order['status'] == Order::STATUS_PAID && $status == Order::STATUS_CANCEL) {
                //已付款订单 取消
                //若已有虚拟商品被核销 则订单不允许退回 积分不退回
                //                $hasCheckSku = OrderSku::where('coding', $orderCoding)
                //                    ->where('is_virtual', OrderSku::COMMON_ON)
                //                    ->whereRaw('`num` > `not_check_num`')
                //                    ->count();
                //                if ($hasCheckSku > 0) {
                //                    throw new RepositoryException('订单部分商品已核销,无法进行此操作');
                //                }

                $refundCoding = generateCode();
                $now          = date('Y-m-d H:i:s');
                //售后记录
                OrderAfterSale::create([
                    'coding'      => $orderCoding,
                    'account_id'  => $order['account_id'],
                    'description' => '取消订单并退款',
                    'created_at'  => $now,
                    'status'      => OrderAfterSale::STATUS_DONE,
                ]);

                //积分日志
                if ($order['score'] > 0) {
                    AccountDataLog::log($order['account_id'], '取消订单退回', $order['score'], AccountDataLog::TYPE_SCORE,
                        AccountDataLog::ACTION_ORDER, $account['score'] + $order['score']);
                }

                //待付款时 积分才退回
                $account->save([
                    'score' => Db::raw('`score` + '.$order['score'])
                ]);

                // 订单金额大于0 退回
                if ($order['price'] > 0) {
                    $order->is_after_sale     = Order::COMMON_ON;
                    $order->after_sale_at     = $now;
                    $order->after_sale_end_at = $now;

                    $result = WechatPay::getInstance()->refund->byOutTradeNumber($orderCoding, $refundCoding, $order['price'] * 100, $order['price'] * 100, [
                        'refund_desc' => '【'.$orderCoding.'】订单退款',
                    ]);
                    if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'FAIL') {
                        //退款不成功
                        self::log('订单退款不成功['.$orderCoding.']'.$result['err_code_des'].'all_msg:'.json_encode($result), null);
                        $order->need_refund = Order::COMMON_ON;//需要退款 但未成功
                    }

                    if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
                        self::log('订单退款成功['.$orderCoding.']'.'all_msg:'.json_encode($result), null);
                    }
                }
                $order->refund_coding = $refundCoding;
                $order->status        = self::STATUS_CLOSED;
                $order->save();
                Db::commit();
                return true;
            }
            */
        } catch (RepositoryException $e) {
            Db::rollback();
            throw $e;
        } catch (Exception $e) {
            Db::rollback();
            throw new RepositoryException($e->getMessage());
        }

    }

    /**
     * 购物车数量
     *
     * @param  string  $type  类型 spu=商品购物车 score=积分商品购物车
     * @param  int  $accountId  用户ID
     * @throws Exception
     */
    public function shoppingCartCount(int $accountId, string $type = 'spu'): int
    {
        $isScore = (int) ($type == 'score');
        event('CheckShoppingCart', $accountId);
        return ShoppingCart::where('is_score', $isScore)->where('account_id', $accountId)->count();
    }


    /**
     * 订单付款
     *
     * @param  string  $orderCoding  订单编号
     * @param  int  $accountId  用户ID
     * @throws RepositoryException|Exception
     */
    public function pay(string $orderCoding, int $accountId): array
    {
        $order = $this->findOneByWhere(['coding' => $orderCoding]);
        if (empty($order)) {
            throw new RepositoryException('没有相关的订单记录');
        }

        if ($order['status'] !== self::STATUS_WAITING) {
            throw new RepositoryException('订单状态已非待付款');
        }

        if ($order['account_id'] != $accountId) {
            throw new RepositoryException('非本人订单');
        }

        //统一下单
        //            $res = $this->wechatMiniPay($order->coding, $account->openid, $order->price);
        $prepayId = $order['prepay_id'];

        //生成小程序支付请求的参数
        $paymentParams = $this->miniPayReqParams($prepayId);
        return [
            'prepay_id'      => $prepayId,
            'payment_params' => $paymentParams,
        ];
    }

    /**
     * 商品sku核验 线下核验
     *
     * @param  string  $orderCoding  订单编号
     * @param  int  $id  订单商品ID =0则核验该订单下所有虚拟商品  >0核验具体记录
     * @param  int  $num  核验数量 仅当$id>0时生效
     * @param  int  $checkBy  核验人account_id  =0则后台无account用户
     * @param  string  $checkType
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     * @throws Exception
     */
    public function check(string $orderCoding, int $id = 0, int $num = 1, int $checkBy = 0, string $checkType = Order::CHECK_TYPE_FRONTEND): bool
    {
        if (!$checkUser = Account::findById($checkBy)) {
            throw new RepositoryException('核验人信息不存在');
        }

        if ($checkUser['is_staff'] != Account::COMMON_ON) {
            throw new RepositoryException('此操作仅限员工');
        }

        $checkUserInfo = sprintf("ID:%d 昵称:%s", $checkUser['id'], $checkUser['nickname']);

        return $this->checkBase($orderCoding, $id, $num, $checkUserInfo, $checkType);
    }

    /**
     * 商品sku核验
     *
     * @param  string  $orderCoding  订单编号
     * @param  int  $id  订单商品ID =0则核验该订单下所有虚拟商品  >0核验具体记录
     * @param  int  $num  核验数量 仅当$id>0时生效
     * @param  string  $checkBy  核验人信息
     * @param  string  $checkType
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     * @throws Exception
     */
    public function checkBase(string $orderCoding, int $id = 0, int $num = 1, string $checkBy = '', string $checkType = Order::CHECK_TYPE_FRONTEND): bool
    {
        if (!$order = Order::findOne(['coding' => $orderCoding])) {
            throw new RepositoryException('订单不存在');
        }

        if (in_array($order['status'], [Order::STATUS_CLOSED, Order::STATUS_WAITING])) {
            // 订单关闭或未付款
            throw new RepositoryException('当前订单状态无法核验');
        }

        $where   = [];
        $where[] = ['coding', '=', $orderCoding];
        $where[] = ['check_type', '=', $checkType];
        $where[] = ['is_virtual', '=', Order::COMMON_ON];//虚拟商品 才能核验
        $where[] = ['is_check', '=', Order::COMMON_OFF];// 非核验完成商品 才能核验
        $where[] = ['not_check_num', '>', Order::COMMON_OFF];// 未核验数量大于0 才能核验
        if ($id > 0) {
            $where[] = ['id', '=', $id];
        }

        $skuList = OrderSku::where($where)->select();

        if ($skuList->isEmpty()) {
            throw new RepositoryException('当前订单无可核验商品');
        }

        $update = [];
        foreach ($skuList as $sku) {
            $arr             = [];
            $arr['id']       = $sku['id'];
            $arr['check_by'] = $checkBy;
            if ($id > 0) {
                // 指定订单商品核验
                if ($id == $sku['id']) {
                    if ($sku['not_check_num'] < $num) {
                        throw new RepositoryException('核验商品数量不足');
                    }

                    if ($sku['not_check_num'] == $num) {
                        $arr['is_check'] = OrderSku::COMMON_ON;
                    }

                    $arr['not_check_num'] = Db::raw('`not_check_num` - '.$num);
                } else {
                    unset($arr);
                }
            } else {
                //订单下所有商品全部核验完
                $arr['is_check']      = OrderSku::COMMON_ON;
                $arr['not_check_num'] = OrderSku::COMMON_OFF;
            }

            if (isset($arr)) {
                $update[] = $arr;
            }
        }

        // 更新订单商品状态
        (new OrderSku())->saveAll($update);

        // 检测订单是否已完成所有核验
        $notCheckNumList = $this->checkAllVirtual($orderCoding);
        $orderUpdate     = [];
        if ($notCheckNumList['total'] == 0) {
            $orderUpdate['virtual_check'] = Order::COMMON_ON;
        }

        if ($notCheckNumList['frontend'] == 0) {
            $orderUpdate['frontend_check'] = Order::COMMON_ON;
        }

        if ($notCheckNumList['backend'] == 0) {
            $orderUpdate['backend_check'] = Order::COMMON_ON;
        }

        if (!empty($orderUpdate)) {
            $order->save($orderUpdate);
        }

        return true;
    }

    /**
     * 商品sku核验检测
     * 检测逻辑:打开商品二维码时,待核验的数量发生变更或为0时 标识本次核验成功
     *
     * @param  string  $orderCoding  订单编号
     * @param  int  $id  订单商品ID =0则核验该订单下所有虚拟商品  >0核验具体记录
     * @param  int  $notCheckNum  未核验的数量
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function checkResult(string $orderCoding, int $id, int $notCheckNum): bool
    {
        if (!$order = Order::findOne(['coding' => $orderCoding])) {
            throw new RepositoryException('订单不存在');
        }

        if (in_array($order['status'], [Order::STATUS_CLOSED, Order::STATUS_WAITING])) {
            // 订单关闭或未付款
            throw new RepositoryException('当前订单状态无法核验');
        }

        if (!$item = OrderSku::findById($id)) {
            throw new RepositoryException('该商品不存在');
        }

        if ($item['coding'] != $orderCoding) {
            throw new RepositoryException('该商品不属于此订单');
        }

        if ($item['not_check_num'] == 0 || $item['not_check_num'] != $notCheckNum) {
            return true;
        }

        return false;
    }

    /**
     * 获取订单虚拟商品未核验数量
     *
     * @param  string  $orderCoding
     * @return array ['total'=所有类型未核验的数量]
     */
    public function checkAllVirtual(string $orderCoding): array
    {
        $where   = [];
        $where[] = ['coding', '=', $orderCoding];
        $where[] = ['is_virtual', '=', Order::COMMON_ON];// 虚拟商品
        $where[] = ['is_check', '=', Order::COMMON_OFF];// 未核验完成
        $where[] = ['not_check_num', '>', Order::COMMON_OFF];// 未核验数量大于0

        $res          = [];
        $res['total'] = 0;
        $countList    = OrderSku::where($where)->group('check_type')->column('count(`id`) as count', 'check_type');
        foreach (Order::checkTypeList() as $type) {
            $res[$type]   = $countList[$type] ?? 0;
            $res['total'] += $res[$type];
        }

        return $res;
    }

    public function userOrderList(int $accountId, array $where = [], int $page = 1, int $size = 0, array $statusArray = [])
    {
        return Order::when(!empty($statusArray), function ($q) use ($statusArray) {
            $q->whereIn('status', $statusArray);
        })->where($where)
            ->when($size > 0, function ($q) use ($page, $size) {
                $q->page($page, $size);
            })
            ->order('id', 'desc')
            ->where('account_id', $accountId)
            ->select();
    }

    /**
     * 获取订单的拼团ID
     *
     * @param  string  $orderCoding
     * @return mixed
     */
    public function groupId(string $orderCoding)
    {
        return OrderGroupList::where('coding', $orderCoding)
            ->where('is_paid', OrderGroupList::COMMON_ON)
            ->value('group_id', 0);
    }


    public function getOrderSku($id)
    {
        return OrderSku::findById($id);
    }
    public function getOrderOriginalPrice($orderCodeing,$skuCoding)
    {
        return OrderSku::where("order_coding",$orderCodeing)->where("coding","<>",$skuCoding)->sum("subtotal");
    }
}