<?php
/**
 * 购物车
 * Created by PhpStorm.
 * User: XiaoMing
 * Date: 2019/11/6
 * Time: 16:20
 */

namespace JinDouYun\Model\Cart;

use JinDouYun\Dao\Cashier\DCashierCustomerPrice;
use JinDouYun\Dao\System\DDeliveryRule;
use JinDouYun\Model\Cashier\MCashierSettings;
use JinDouYun\Model\Market\MComBinPackage;
use Mall\Framework\Core\ErrorCode;
use Mall\Framework\Core\ResultWrapper;
use Mall\Framework\Core\StatusCode;

use JinDouYun\Dao\Cart\DCart;
use JinDouYun\Cache\ActivityLimitCache;
use JinDouYun\Controller\Common\Logger;
use JinDouYun\Model\Market\MVipCard;
use JinDouYun\Model\System\MBasicSetup;
use JinDouYun\Model\Market\MActivity;
use JinDouYun\Model\Market\MUserCoupon;
use JinDouYun\Model\System\MEnterpriseBindPayment;
use JinDouYun\Model\Customer\MCustomer;
use JinDouYun\Model\Customer\MShippingAddress;
use JinDouYun\Model\GoodsManage\MSku;
use JinDouYun\Model\Price\MPrice;
use JinDouYun\Model\Stock\MInventory;
use JinDouYun\Model\SysAreaChina\MSysAreaChina;
use JinDouYun\Model\System\MDeliverySetting;
use JinDouYun\Model\Goods\MGoods;
use JinDouYun\Model\Shop\MShop;
use JinDouYun\Cache\CustomerCache;
use JinDouYun\Cache\GoodsBasicRelevant;
use JinDouYun\Dao\Customer\DCustomer;
use JinDouYun\Model\Cart\MPreferentialProcess;

class MCart
{
    /**
     * @var DCart
     */
    private $objDCart;

    /**
     * @var int 用户id
     */
    private $onlineUserId;

    /**
     * @var int 企业id
     */
    private $onlineEnterpriseId;

    /**
     * @var MCustomer
     */
    private $objMCustomer;

    /**
     * @var ActivityLimitCache
     */
    private $objActivityLimitCache;

    /**
     * @var bool
     */
    private $isFront;//是否是前台调用此Model 前台=>true

    /**
     * @var int 客户id
     */
    private $customerId;//用户对应的客户id

    /**
     * @var MActivity
     */
    private $objMActivity;

    /**
     * @var array 失效商品集合
     */
    private $invalidData = [];

    /**
     * @var array 操作类型
     */
    private static $type = [
        'single' => 1,//单个商品
        'shop'   => 2,//按店铺
        'all'    => 3,//全选
    ];

    /**
     * @var array 阶梯价
     */
    private static $enabledLadder = [
        'open'  => 1,//启用阶梯价
        'close' => 0,//未启用阶梯价
    ];

    /**
     * @var int[] 立即购买来源业务
     */
    public static $sourceType = [
        'comBin' => 1,//组合套餐
        'goods'  => 2, // 普通商品
    ];

    /**
     * @var MSku
     */
    private $objMSku;

    /**
     * @var MGoods
     */
    private $objMGoods;

    /**
     * @var MBasicSetup
     */
    private $objMBasicSetup;

    /**
     * @var int
     */
    private $onlineUserDefaultDeliveryType = null;

    /**
     * @var float
     */
    private $onlineUserAddressCode = null;

    /**
     * @var float
     */
    private $freeExpressPrice = null;

    /**
     * @var string
     */
    private $cashierUId;

    /**
     * @var DCustomer
     */
    private $objDCustomer;

    /**
     * @var MCashierSettings
     */
    private $objMCashierSettings;

    /**
     * @var DCashierCustomerPrice
     */
    private $objDCashierCustomerPrice;

    /**
     * @var boolean
     */
    private $isCashier;

    /**
     * @var float 运费金额
     */
    private $expressMoney = 0;

    /**
     * @var array 店铺运费
     */
    private $expressShopMoney = [];

    /**
     * @var array
     */
    private $selectStateMap = [];

    /**
     * @var bool
     */
    private $preSale;


    /**
     * MCart constructor.
     * @param $onlineUserId
     * @param $onlineEnterpriseId
     * @param bool $isFront
     * @param string $areaCode
     * @param string $cashierUId
     * @param boolean $isCashier
     * @throws \Exception
     */
    public function __construct($onlineUserId, $onlineEnterpriseId, $isFront = false, $areaCode = '', $cashierUId = '', $isCashier = false)
    {
        $this->isCashier = $isCashier;
        $this->cashierUId = $cashierUId;
        $this->isFront = $isFront;
        $this->onlineUserId = $onlineUserId;
        $this->onlineEnterpriseId = $onlineEnterpriseId;
        $this->objDCart = new DCart('default');
        $this->objDCart->setTable($this->objDCart->get_Table() . '_' . $onlineEnterpriseId);
        $this->objMCustomer = new MCustomer($this->onlineEnterpriseId, $this->onlineUserId);
        $this->objActivityLimitCache = new ActivityLimitCache($this->onlineEnterpriseId);
        $this->objMActivity = new MActivity($this->onlineUserId, $this->onlineEnterpriseId, $areaCode);
        $this->objMSku = new MSku($this->onlineUserId, $this->onlineEnterpriseId);
        $this->objMGoods = new MGoods($this->onlineEnterpriseId, $this->isFront, $this->onlineUserId);
        $this->objMBasicSetup = new MBasicSetup($this->onlineEnterpriseId);
        $this->objDCustomer = new DCustomer();
        $this->objDCustomer->setTable('qianniao_customer_' . $this->onlineEnterpriseId);
        $this->objMCashierSettings = new MCashierSettings($this->onlineUserId, $this->onlineEnterpriseId);
        $this->objDCashierCustomerPrice = new DCashierCustomerPrice();
        $this->objDCashierCustomerPrice->setTable('qianniao_cashier_customer_price_' . $this->onlineEnterpriseId);
        self::getCustomerInfo();
    }

    /**
     * Doc: (des="")
     * User: XMing
     * Date: 2020/12/25
     * Time: 11:50 上午
     * @return ResultWrapper
     */
    public function getPreSale(): ResultWrapper
    {
        $objMBasicSetup = new MBasicSetup($this->onlineEnterpriseId);
        $setResult = $objMBasicSetup->getBasicField('preSale');
        $this->preSale = StatusCode::$delete;
        if (!$setResult->isSuccess()) {
            return ResultWrapper::fail($setResult->getData(), $setResult->getErrorCode());
        }
        $set = (array)$setResult->getData();
        $this->preSale = getArrayItem($set, 'preSale', StatusCode::$delete);
        if (empty($this->preSale)) $this->preSale = StatusCode::$delete;
        return ResultWrapper::success($set);
    }

    /**
     * 获取客户id
     */
    private function getCustomerInfo()
    {
        if ($this->isFront === false) {
            $this->customerId = $this->onlineUserId;//TODO()
        } else {
            $this->customerId = $this->objMCustomer->getCustomerIdByUserCenterId($this->onlineUserId);
        }
    }

    /**
     * 根据限购数量对数据进行分组
     * @param $data
     * @return array
     */
    private function checkLimitGroup($data)
    {
        if (empty($data)) return $data;
        $allMapping = [];
        foreach ($data as &$goods) {
            if ($goods['isActivityPrice'] == StatusCode::$standard) {
                $userLimit = $this->objActivityLimitCache->getLimit($goods['activityId'], $goods['goodsId'], $goods['skuId'], $this->onlineUserId);//用户限购数量
                $allowNum = $goods['limitNum'] - $userLimit;//还可以购买的数量
                $buyNum = $goods['buyNum'];//购买数量
                if ($buyNum >= $allowNum) {
                    //拆分
                    $goods['buyNum'] = $allowNum;
                    $allMapping[] = $goods;
                    $newGoods = $goods;
                    if ($buyNum - $allowNum > 0) {
                        $newGoods['buyNum'] = $buyNum - $allowNum;
                        $newGoods['activityId'] = 0;//将活动id重置为0
                        $newGoods['isActivityPrice'] = StatusCode::$delete;
                        $allMapping[] = $newGoods;
                    }
                } else {
                    //限购等于加入数量
                    $allMapping[] = $goods;
                }
            } else {
                $allMapping[] = $goods;
            }
        }
        return $allMapping;
    }

    /**
     * Doc: (des="小程序端加入购物车，最新")
     * User: XMing
     * Date: 2020/8/4
     * Time: 2:35 下午
     * @param array $params
     * @return ResultWrapper
     * @throws \Exception
     */
    public function addCartApi(array $params)
    {
        if (empty( $params['goodsData'] )) {
            return ResultWrapper::fail('加入购物车数据为空', ErrorCode::$paramError);
        }
        
        foreach ( $params['goodsData'] as $key => $data ){
            // 从商品表查询商品信息
            $goodsInfoResult = $this->objMGoods->getGoodsInfo($data['goodsId']);
            if (!$goodsInfoResult->isSuccess()) {
                return ResultWrapper::fail($goodsInfoResult->getData(), $goodsInfoResult->getErrorCode());
            }
            $goodsInfo = $goodsInfoResult->getData();

            // 验证此商品是否有效
            $checkResult = self::checkGoods($goodsInfo, $data, 'add');
            unset($data);
            if (!$checkResult->isSuccess()) {
                return ResultWrapper::fail($checkResult->getData(), $checkResult->getErrorCode());
            }
            $data = $checkResult->getData();


            // 判断此商品是否是活动商品
            $addMap = [
                'goodsBasicId' => $data['goodsBasicId'],
                'goodsId'      => $data['goodsId'],
                'skuId'        => $data['skuId'],
                'buyNum'       => $data['buyNum'],
                'shopId'       => $data['shopId'],
                'source'       => $data['source'],
                'setNum'       => $data['setNum'],
                'activityId'   => $data['activityId'],
                'goodsCode'    => $data['goodsCode'],
                'warehouseId'  => $data['warehouseId']
            ];
            // 将加入购物车的数据进行分组[old=>在购物车中已存在,now=>新加入购物车商品]
            $groupResult = self::existCartAndGroup([$addMap]);
            if (!$groupResult->isSuccess()) {
                return ResultWrapper::fail($groupResult->getData(), $groupResult->getErrorCode());
            }
            $groupData = $groupResult->getData();
            if (empty($groupData)) {
                return ResultWrapper::fail('数组分组失败', ErrorCode::$paramError);
            }
            // 在购物车中已存在的商品
            $updates = isset($groupData['old']) ? $groupData['old'] : [];
            // 新加入购物车商品
            $inserts = isset($groupData['now']) ? $groupData['now'] : [];
            
            // 操作数据库
            $this->objDCart->beginTransaction();
            // 需要更新的数据
            if (!empty($updates)) {
                $update = array_shift($updates);
                $tableName = 'qianniao_cart_' . $this->onlineEnterpriseId;
                $sql = 'UPDATE ' . $tableName . ' SET buyNum = buyNum+' . $update['buyNum'] . ' 
                    WHERE goodsId = ' . $update['goodsId'] . ' AND 
                    skuId = ' . $update['skuId'] . ' AND 
                    userCenterId = ' . $this->onlineUserId . ' AND 
                    activityId = ' . $update['activityId'];
                $updateRes = $this->objDCart->query($sql);
                if ($updateRes === false) {
                    $this->objDCart->rollBack();
                    return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                }
            }

            // 需要插入的数据
            if (!empty($inserts)) {
                $insert = array_shift($inserts);
                $insertMap = [
                    'userCenterId' => $this->onlineUserId,
                    'selection'    => StatusCode::$standard,
                    'goodsBasicId' => $insert['goodsBasicId'],
                    'goodsId'      => $insert['goodsId'],
                    'skuId'        => $insert['skuId'],
                    'buyNum'       => $insert['buyNum'],
                    'shopId'       => $insert['shopId'],
                    'source'       => $insert['source'],
                    'activityId'   => $insert['activityId'],
                    'goodsCode'    => $insert['goodsCode'],
                    'warehouseId'  => $insert['warehouseId']
                ];//要插入的数据
                $insertRes = $this->objDCart->insert($insertMap);
                if ($insertRes === false) {
                    $this->objDCart->rollBack();
                    return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                }
            }
        }

        $this->objDCart->commit();
        return ResultWrapper::success('加入成功');
    }

    /**
     * Doc: (des="检测商品")
     * User: XMing
     * Date: 2020/8/4
     * Time: 3:21 下午
     * @param array $goodsData
     * @param array $addCart
     * @param string $action
     * @return ResultWrapper
     */
    private function checkGoods(array $goodsData, array $addCart, $action = 'add')
    {
        if (empty($goodsData)) {
            return ResultWrapper::fail('商品信息为空', ErrorCode::$paramError);
        }
        //验证商品的状态以及库存
        if ($goodsData['enableStatus'] == StatusCode::$delete || $goodsData['deleteStatus'] == StatusCode::$delete) {
            return ResultWrapper::fail('商品已下架', ErrorCode::$paramError);
        }
        $specMultiple = $goodsData['specMultiple'];//spec数据
        if (empty($specMultiple)) {
            return ResultWrapper::fail('商品规格信息获取失败', ErrorCode::$paramError);
        }
        $specMultipleMap = [];//商品规格映射数据
        foreach ($specMultiple as $value) {
            $specMultipleMap[$value['id']] = $value;
        }
        if (!isset($specMultipleMap[$addCart['skuId']])) {
            return ResultWrapper::fail('规格信息不存在', ErrorCode::$paramError);
        }

        $subBuyNumResult = self::getCartBuyNumBySku($addCart['skuId'], $this->onlineUserId);
        if (!$subBuyNumResult->isSuccess()) {
            return ResultWrapper::fail($subBuyNumResult->getData(), $subBuyNumResult->getErrorCode());
        }
        $subBuyNum = floatval($subBuyNumResult->getData());

        //是否开启预售
        switch ($action){
            case 'add':
                if ($goodsData['isDistribution'] == StatusCode::$delete){
                    if ($specMultipleMap[$addCart['skuId']]['inventory'] < $addCart['buyNum'] + $subBuyNum) {
                        //取库存最大数
                        $allowNum = bcsub($specMultipleMap[$addCart['skuId']]['inventory'], $subBuyNum);//最多还可以增加的数量
                        if ($allowNum <= 0) {
                            $addCart['buyNum'] = 0;
                        } else {
                            $addCart['buyNum'] = $allowNum;
                        }
                        //return ResultWrapper::fail('商品库存不足', ErrorCode::$paramError);
                    }
                }
                break;
            case 'update':
                if ($goodsData['isDistribution'] == StatusCode::$delete){
                    if ($specMultipleMap[$addCart['skuId']]['inventory'] < $addCart['buyNum']) {
                        return ResultWrapper::success($specMultipleMap[$addCart['skuId']]['inventory']);
                    } else {
                        return ResultWrapper::success($addCart['buyNum']);
                    }
                }else{
                    return ResultWrapper::success($addCart['buyNum']);
                }
                break;
        }

        $addCart['isActivity'] = $specMultipleMap[$addCart['skuId']]['isActivity'];//是否是活动商品
        $addCart['setNum'] = $specMultipleMap[$addCart['skuId']]['setNum'];//起订数量
        $addCart['warehouseId'] =getArrayItem($goodsData,'warehouseId','');
        switch ($specMultipleMap[$addCart['skuId']]['isActivity']) {
            case StatusCode::$standard:
                $addCart['activityId'] = $specMultipleMap[$addCart['skuId']]['activity']['activityId'];
                $addCart['limitNum'] = $specMultipleMap[$addCart['skuId']]['activity']['limitNum'];//商品活动限购数量
                break;
            case StatusCode::$delete:
                $addCart['activityId'] = 0;
                break;
        }
        return ResultWrapper::success($addCart);
    }

    /**
     * app加入购物车
     * @param $params
     * @return ResultWrapper
     * @throws \Exception
     */
    public function addCart($params)
    {
        $this->objDCart->beginTransaction();
        $goodsData = $params['goodsData'];

        $dbResult = self::checkCart($goodsData);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), $dbResult->getErrorCode());
        }
        $checkData = $dbResult->getData();

        //前台验证活动商品剩余数量,和用户限购数量
        if ($this->isFront === true) {
            //将数据拆分
            $checkData = self::checkLimitGroup($checkData);
            $dbResult = self::checkLimit($checkData);
            if (!$dbResult->isSuccess()) {
                return ResultWrapper::fail($dbResult->getData(), $dbResult->getErrorCode());
            }
            $dbResult = $dbResult->getData();
            $mapping = $dbResult['mapping'];
            $checkData = $dbResult['checkData'];
        }

        unset($dbResult);
        $dbResult = self::existCartAndGroup($checkData);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), $dbResult->getErrorCode());
        }
        $cartData = $dbResult->getData();//分组后的数据
        unset($dbResult);

        $dbResult = true;//初始化
        $oldCart = $cartData['old'];//旧的数据
        $nowCart = $cartData['now'];//新的数据
        if (!empty($oldCart)) {
            foreach ($oldCart as $key => $val) {
                $sql = "UPDATE qianniao_cart_{$this->onlineEnterpriseId} SET buyNum=buyNum+{$val['buyNum']} WHERE goodsId={$val['goodsId']} AND skuId={$val['skuId']} AND userCenterId={$this->onlineUserId} AND activityId={$val['activityId']}";
                $dbResult = $this->objDCart->query($sql);
            }
            if ($dbResult === false) {
                $this->objDCart->rollBack();
                return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
            }
        }

        if (!empty($nowCart)) {
            $insert = [];
            foreach ($nowCart as $key => $val) {
                $insert[] = [
                    'userCenterId' => $this->onlineUserId,
                    'selection'    => StatusCode::$standard,
                    'skuId'        => $val['skuId'],
                    'goodsCode'    => $val['goodsCode'],
                    'goodsId'      => $val['goodsId'],
                    'shopId'       => $val['shopId'],
                    'buyNum'       => $val['buyNum'],
                    'source'       => $val['source'],
                    'goodsBasicId' => $val['goodsBasicId'],
                    'warehouseId'  => $val['warehouseId'],
                    'activityId'   => isset($val['activityId']) ? $val['activityId'] : 0,//活动id
                ];
            }
            $dbResult = $this->objDCart->insert($insert, true);
            if ($dbResult === false) {
                $this->objDCart->rollBack();
                return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
            }
        }

        $objCustomerCache = new CustomerCache();
        $objCustomerCache->incrInterestCustomer($this->customerId, $this->onlineEnterpriseId);

        //用户限购缓存
        if (!empty($mapping)) {
            self::userLimit($mapping);
        }
        $this->objDCart->commit();
        return ResultWrapper::success($dbResult);
    }

    /**
     * 后台加入购物车
     * @param $params
     * @return ResultWrapper
     * @throws \Exception
     */
    public function manageAddCart($params)
    {
        $this->objDCart->beginTransaction();
        $goodsData = $params['goodsData'];

        $dbResult = self::checkCart($goodsData);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), $dbResult->getErrorCode());
        }
        $checkData = $dbResult->getData();//验证后数据
        $dbResult = $this->existCartAndGroup($checkData);//数据分组
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), $dbResult->getErrorCode());
        }
        $cartData = $dbResult->getData();//分组后的数据
        unset($dbResult);

        $dbResult = true;//初始化
        $update = $cartData['old'];//旧的数据
        $insert = $cartData['now'];//新的数据

        //更新数量
        if (!empty($update)) {
            foreach ($update as $row) {
                $dbResult = $this->objDCart->set_inc(
                    'buyNum',
                    [
                        'goodsId'      => $row['goodsId'],
                        'skuId'        => $row['skuId'],
                        'userCenterId' => $this->onlineUserId,
                        'activityId'   => $row['activityId'],
                    ],
                    $row['buyNum']
                );
                if ($dbResult === false) {
                    $this->objDCart->rollBack();
                    return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                }
            }
        }

        //insert
        if (!empty($insert)) {
            $add = [];
            foreach ($insert as $row) {
                $add[] = [
                    'userCenterId' => $this->onlineUserId,
                    'selection'    => StatusCode::$standard,
                    'skuId'        => $row['skuId'],
                    'goodsCode'    => $row['goodsCode'],
                    'goodsId'      => $row['goodsId'],
                    'shopId'       => $row['shopId'],
                    'buyNum'       => $row['buyNum'],
                    'source'       => $row['source'],
                    'goodsBasicId' => $row['goodsBasicId'],
                    'warehouseId'  => $row['warehouseId'],
                    'activityId'   => isset($val['activityId']) ? $row['activityId'] : 0,//活动id
                ];
            }
            $dbResult = $this->objDCart->insert($add, true);
            if ($dbResult === false) {
                $this->objDCart->rollBack();
                return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
            }
        }

        $objCustomerCache = new CustomerCache();
        $objCustomerCache->incrInterestCustomer($this->customerId, $this->onlineEnterpriseId);

        $this->objDCart->commit();
        return ResultWrapper::success($dbResult);
    }


    /**
     * adj 1:+ 2:-
     * isUpdate true  false
     * step 调整量
     * @param $checkData
     * @param int $adj
     * @param bool $isUpdate
     * @return ResultWrapper
     */
    private function checkLimit($checkData, $adj = 1, $isUpdate = false, $step = null)
    {
        if (empty($checkData)) return ResultWrapper::success($checkData);
        $mapping = [];
        foreach ($checkData as $key => $goods) {
            $skuData = $goods['specMultiple'];
            $skuMapping = [];
            foreach ($skuData as $sku) {
                $skuMapping[$sku['id']] = $sku;
            }
            $thisSku = $skuMapping[$goods['skuId']];//当前加入购物车规格sku详情
            if (
                $goods['isActivityPrice'] == StatusCode::$delete ||
                ($thisSku['isActivity'] == StatusCode::$delete && $thisSku['activityId'] == 0)) {
                //活动商品,已购买完,或已限购,将以原价购买
                continue;
            }
            $activityId = $goods['activityId'];//活动id
            $activityDetails = $this->objActivityLimitCache->getActivity($activityId);
            if (empty($activityDetails)) {
                return ResultWrapper::fail('活动不存在', ErrorCode::$paramError);
            }
            if ($activityDetails['startTime'] > time()) {
                return ResultWrapper::fail($goods['title'] . '活动尚未开始', ErrorCode::$paramError);
            }
            if ($activityDetails['endTime'] < time()) {
                return ResultWrapper::fail($goods['title'] . '活动已结束', ErrorCode::$paramError);
            }
            //获取活动商品剩余数量
            $surplusNum = $this->objActivityLimitCache->getLen($activityId, $goods['goodsId'], $goods['skuId']);
            if ($surplusNum == 0) {
                return ResultWrapper::fail($goods['title'] . '抢光了', ErrorCode::$paramError);
            }
            if ($surplusNum < $goods['buyNum']) {
                return ResultWrapper::fail($goods['title'] . '活动剩余数量不足', ErrorCode::$paramError);
            }

            //获取每人限购数量
            $userLimit = self::getLimitNum($activityDetails, $goods['goodsId'], $goods['skuId']);//用户限购数量
            $userSurplusNum = $this->objActivityLimitCache->getLimit($activityId, $goods['goodsId'], $goods['skuId'], $this->onlineUserId);
            $buyNum = 0;//初始化
            if ($isUpdate) {
                if ($adj == 1) {
                    //加操作
                    if ($userLimit < ($userSurplusNum + $step)) {
                        return ResultWrapper::fail($goods['title'] . '每人限购' . $userLimit . $thisSku['unitName'], ErrorCode::$paramError);
                    }
                }
                $buyNum = $userSurplusNum + $step;
            } else {
                //addCart
                if ($userLimit < ($userSurplusNum + $goods['buyNum'])) {
                    return ResultWrapper::fail($goods['title'] . '每人限购' . $userLimit . $thisSku['unitName'], ErrorCode::$paramError);
                }
                $buyNum = $userSurplusNum + $goods['buyNum'];//
            }

            $mapping[] = [
                'activityId' => $activityId,
                'goodsId'    => $goods['goodsId'],
                'skuId'      => $goods['skuId'],
                'buyNum'     => $buyNum,
            ];
        }
        $data = [
            'checkData' => $checkData,
            'mapping'   => $mapping,
        ];
        return ResultWrapper::success($data);
    }

    /**
     * 缓存用户限购
     * @param $data
     * @return mixed
     */
    private function userLimit($data)
    {
        $result = false;
        foreach ($data as $goods) {
            $result = $this->objActivityLimitCache->writeLimit($goods['activityId'], $goods['goodsId'], $goods['skuId'], $this->onlineUserId, $goods['buyNum']);
        }
        return $result;
    }

    /**
     * 获取商品的限购数量
     * @param $data
     * @param $goodsId
     * @param $skuId
     * @return int|mixed
     */
    private function getLimitNum($data, $goodsId, $skuId)
    {
        if (empty($data)) return $data;
        $goodsData = $data['activityGoods'];
        $mapping = [];
        foreach ($goodsData as $goods) {
            $mapping[$goods['goodsId'] . $goods['skuId']] = $goods;
        }
        return isset($mapping[$goodsId . $skuId]['limitNum']) ? $mapping[$goodsId . $skuId]['limitNum'] : 0;
    }


    /**
     * 检测购物车商品(最新的方法)
     * @param $data
     * @return ResultWrapper
     * @throws \Exception
     */
    public function checkCart($data)
    {
        if (empty($data)) return ResultWrapper::fail('购物车数据为空', ErrorCode::$paramError);
        $objMGoods = new MGoods($this->onlineEnterpriseId, $this->isFront, $this->onlineUserId);
        //验证商品数据
        foreach ($data as $key => $val) {
            //获取商品详情
            $details = $objMGoods->getGoodsInfo($val['goodsId']);
            if (!$details->isSuccess()) {
                return ResultWrapper::fail($details->getData(), $details->getErrorCode());
            }
            $goods = $details->getData();//商品数据
            if (empty($details)) {
                return ResultWrapper::fail($val['goodsCode'] . '查询商品失败', ErrorCode::$paramError);
            }
            //验证商品状态
            if ($goods['enableStatus'] == StatusCode::$delete || $goods['deleteStatus'] == StatusCode::$delete) {
                return ResultWrapper::fail($goods['title'] . ' 已下架', ErrorCode::$paramError);
            }

            $specMultiple = $goods['specMultiple'];//spec数据
            $specMapping = [];//Map
            foreach ($specMultiple as $spec) {
                $specMapping[$spec['id']] = $spec;
            }
            if (!isset($specMapping[$val['skuId']])) {
                Logger::logs(E_USER_ERROR, '规格获取失败', __CLASS__, __LINE__, $specMapping);
                return ResultWrapper::fail($goods['title'] . '规格获取失败', ErrorCode::$paramError);
            }

            //验证商品库存
            if ($this->preSale == StatusCode::$delete) {
                if ($specMapping[$val['skuId']]['inventory'] < $val['buyNum']) {
                    $specName = self::createSpecName($specMapping[$val['skuId']]);
                    return ResultWrapper::fail($goods['title'] . $specName . ' 库存不足', ErrorCode::$paramError);
                }
            }
            $data[$key]['basicGoodsId'] = $val['goodsBasicId'];//为了兼容之前的代码
            $data[$key]['skuData'] = [];//兼容之前数据
            $data[$key]['specMultiple'] = $specMultiple;//spec
            $data[$key]['warehouseId'] = $goods['warehouseId'];//店铺所在仓库
            $data[$key]['title'] = $goods['title'];//商品名称
            $data[$key]['setNum'] = $specMapping[$val['skuId']]['setNum'];//加入购物车起订量
            $data[$key]['activityId'] = 0;//初始化活动id
            //todo（优化秒杀商品活动）
            if ($specMapping[$val['skuId']]['isActivity'] == StatusCode::$standard) {
                //商品存在活动时,没有起订量
                $data[$key]['setNum'] = 1;
            }
            $data[$key]['inventory'] = $specMapping[$val['skuId']]['inventory'];//加入购物车商品规格的可用库存
            $data[$key]['isActivityPrice'] = $specMapping[$val['skuId']]['isActivityPrice'];
            if ($specMapping[$val['skuId']]['isActivityPrice'] == StatusCode::$standard) {
                $data[$key]['activityId'] = $specMapping[$val['skuId']]['activity']['activityId'];
            }
            $data[$key]['limitNum'] = $specMapping[$val['skuId']]['limitNum'];//商品限购数量
        }

        return ResultWrapper::success($data);
    }

    /**
     * 生成属性名
     * @param $data
     * @return mixed|string
     */
    public static function createSpecName($data)
    {
        if (empty($data)) {
            return '';
        }
        $unitName = $data['unitName'];
        if (!empty($data['specGroup'])) {
            $arrSpecName = array_column($data['specGroup'], 'specValueName');
            $specName = rtrim(implode('_', $arrSpecName), '_');
            $unitName .= '_' . $specName;
        }
        return $unitName;

    }

    /**
     * 将加入购物车的数据进行分组[old=>在购物车中已存在,now=>新加入购物车商品]
     * @param $nowData [加入购物车的数据]
     * @return ResultWrapper
     */
    private function existCartAndGroup($nowData)
    {
        // 查询当前购物车所有商品数据
        $dbResult = self::getCartByUid();
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail('获取购物车数据失败', ErrorCode::$dberror);
        }
        $oldCartData = $dbResult->getData();

        // 对所有商品数据生成hash值，用于判断新加入的商品是否已经存在于购物车之中了
        $allHash = [];
        foreach ($oldCartData as $key => $val) {
            $allHash[] = md5($val['goodsId'] . $val['shopId'] . $val['skuId'] . $val['activityId']);
        }

        $old = [];//旧的数据
        $now = [];//新的数据
        foreach ($nowData as $key => &$val) {
            $md5 = md5($val['goodsId'] . $val['shopId'] . $val['skuId'] . $val['activityId']);
            if (in_array($md5, $allHash)) {
                $old[] = $val;
            } else {
                //之前购物车中没有此商品,检查起订量
                if ($val['setNum'] != 0) {
                    //具有起订量
                    if ($val['buyNum'] < $val['setNum']) {
                        $val['buyNum'] = $val['setNum'];
                    }
                }
                $now[] = $val;
            }
        }
        $groupData = [
            'old' => $old,
            'now' => $now
        ];
        return ResultWrapper::success($groupData);
    }

    /**
     * Doc: (des="更新购车商品数量")
     * User: XMing
     * Date: 2020/8/6
     * Time: 9:43 上午
     * @param array $params
     * @return ResultWrapper
     * @throws \Exception
     */
    public function updateBuyNumApi(array $params)
    {
        // 如果数量改成0则删除
        if (bccomp($params['buyNum'], 0, 0) === 0) {
            $dbResult = $this->objDCart->delete(['id' => $params['cartId']]);
            if ($dbResult === false) {
                return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
            }
            return ResultWrapper::success('操作成功');
        }


        $cart = $this->objDCart->get($params['cartId']);
        if ($cart === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        if (empty($cart)) {
            return ResultWrapper::success(true);
        }

        $goodsInfoResult = $this->objMGoods->getGoodsInfo($cart['goodsId']);
        if (!$goodsInfoResult->isSuccess()) {
            return ResultWrapper::fail($goodsInfoResult->getData(), $goodsInfoResult->getErrorCode());
        }
        $goodsInfo = $goodsInfoResult->getData();
        $checkResult = self::checkGoods($goodsInfo, [
            'skuId'  => $cart['skuId'],
            'buyNum' => $params['buyNum'],
        ], 'update');
        unset($data);
        if (!$checkResult->isSuccess()) {
            return ResultWrapper::fail($checkResult->getData(), $checkResult->getErrorCode());
        }
        $allBuyNum = $checkResult->getData();
        $params['buyNum'] = $allBuyNum;
        $dbResult = $this->objDCart->update(['buyNum' => $params['buyNum']], $params['cartId']);
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success('操作成功');
    }

    /**
     * 修改购物车商品购买数量
     * @param $params
     * @return ResultWrapper
     * @throws \Exception
     */
    public function updateBuyNum($params)
    {
        if (bccomp($params['buyNum'], 0, 0) === 0) {
            $dbResult = $this->objDCart->delete(['id' => $params['cartId']]);
            if ($dbResult === false) {
                return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
            }
            return ResultWrapper::success('操作成功');
        } else {
            $goods = self::getCartInventory(['id' => $params['cartId']]);
            if (!$goods->isSuccess()) {
                return ResultWrapper::fail($goods->getData(), ErrorCode::$dberror);
            }
            $goods = $goods->getData();
            $inventoryNum = isset($goods[0]['inventory']) ? $goods[0]['inventory'] : 0;//商品剩余库存
            $setNum = isset($goods[0]['setNum']) ? $goods[0]['setNum'] : 0;//商品规格起订数量
            $title = isset($goods[0]['title']) ? $goods[0]['title'] : '';//商品名称
            if (bccomp($inventoryNum, $params['buyNum'], 8) === -1) {
                return ResultWrapper::fail('商品库存不足', ErrorCode::$paramError);
            }
            if (bccomp($params['buyNum'], $setNum, 0) === -1) {
                return ResultWrapper::fail($title . '最小起订数量' . $setNum, ErrorCode::$paramError);
            }

            if ($this->isFront) {
                $dbResult = $this->objDCart->get(['id' => $params['cartId']]);
                if ($dbResult === false) {
                    return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                }
                $step = bcsub($params['buyNum'], $dbResult['buyNum'], 0);//调整量
                //所有减操作正常进行
                if (bccomp($params['buyNum'], $dbResult['buyNum']) === 0) {
                    return ResultWrapper::success('操作成功');
                } elseif (bccomp($params['buyNum'], $dbResult['buyNum'], 0) === -1) {
                    $adj = 2;//减操作
                    $result = $this->objDCart->update(['buyNum' => $params['buyNum']], ['id' => $params['cartId']]);
                    if ($result === false) {
                        return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                    }
                    return ResultWrapper::success('操作成功');
                } elseif (bccomp($params['buyNum'], $dbResult['buyNum'], 0) === 1) {
                    $adj = 1;//加操作
                    $insert = [
                        'goodsId'      => $dbResult['goodsId'],
                        'goodsBasicId' => $dbResult['goodsBasicId'],
                        'shopId'       => $dbResult['shopId'],
                        'source'       => $dbResult['source'],
                        'skuId'        => $dbResult['skuId'],
                        'buyNum'       => $step,
                        'goodsCode'    => $dbResult['goodsCode']
                    ];
                    $result = self::addCart(['goodsData' => [$insert]]);
                    if (!$result->isSuccess()) {
                        return ResultWrapper::fail($result->getData(), $result->getErrorCode());
                    }
                    return ResultWrapper::success('操作成功');
                }
            } else {
                $dbResult = $this->objDCart->update(['buyNum' => $params['buyNum']], $params['cartId']);
                if ($dbResult === false) {
                    return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                }
                return ResultWrapper::success('操作成功');
            }
        }
    }

    /**
     * Doc: (des="获取用户的购物车数据")
     * User: XMing
     * Date: 2020/8/5
     * Time: 3:15 下午
     * @param integer $isZero
     * @param boolean $isCashier
     * @return ResultWrapper
     * @throws \Exception
     */
    public function getCartByUserCenterIdApi(): ResultWrapper
    {
        // 查询购物车数据
        $sql = 'SELECT id,goodsId,goodsCode,buyNum,shopId,source,goodsBasicId,selection,skuId,warehouseId,activityId,cashierUid FROM qianniao_cart_'.$this->onlineEnterpriseId.' WHERE userCenterId = '.$this->onlineUserId.' FOR UPDATE';
        $cartList = $this->objDCart->query($sql);
        if ($cartList === false){
            return ResultWrapper::fail($this->objDCart->error(),ErrorCode::$dberror);
        }

        if (empty($cartList)) {
            return ResultWrapper::success(['data' => [], 'total' => 0]);
        }

        // 提取活动ids
        $activityIds = [];
        foreach ($cartList as $item){
            if(!empty($item['activityId']) && !in_array($item['activityId'],$activityIds)){
                $activityIds[] = $item['activityId'];
            }
        }
        if (!empty($activityIds)){
            $groupResult = self::cartGroupData($cartList);
            if (!$groupResult->isSuccess()) {
                return ResultWrapper::fail($groupResult->getData(), $groupResult->getErrorCode());
            }
            $cartList = $groupResult->getData();
            if (empty($cartList)) {
                return ResultWrapper::success(['data' => [], 'total' => 0]);
            }
        }
        $dataResult = self::formatGoodsAndShop($cartList);
        if (!$dataResult->isSuccess()) {
            return ResultWrapper::fail($dataResult->getData(), $dataResult->getErrorCode());
        }
        $data = $dataResult->getData();
        $return = [
            'data'  => $data,
            'total' => empty($data) ? 0 : count($data),
        ];
        return ResultWrapper::success($return);
    }

    /**
     * Doc: (des="抹零操作")
     * User: XMing
     * Date: 2020/9/12
     * Time: 7:08 下午
     * @param array $data
     * @param int $isZero
     * @return ResultWrapper
     */
    private function buildZero($data, $isZero)
    {
        //是否开启抹零
        $data['rem_money'] = '0.00';
        $settingResult = $this->objMCashierSettings->get();
        if (!$settingResult->isSuccess()) {
            return ResultWrapper::fail($settingResult->getData(), $settingResult->getErrorCode());
        }
        $setting = $settingResult->getData();
        if (!is_array($setting)) {
            //不是数组时，说明未配置
            return ResultWrapper::success($data);
        }
        if (!isset($setting['add_form'])) {
            return ResultWrapper::fail('收银台配置错误', ErrorCode::$paramError);
        }
        if (!isset($setting['add_form']['zero_set'])) {
            return ResultWrapper::fail('收银台参数配置错误', ErrorCode::$paramError);
        }
        $zeroSet = $setting['add_form']['zero_set'];
        if ($zeroSet['status'] == StatusCode::$delete) {
            //没有开启抹零,前台需要在抹零那判断是否开启
            return ResultWrapper::success($data);
        }
        $payMoney = $data['payMoney'];
        if ($zeroSet['auto_zero'] == StatusCode::$standard) {
            //开启了自动抹零
            switch ($zeroSet['type']) {
                case 1:
                    //分
                    $newPatMoney = bcadd($payMoney, 0, 1);
                    break;
                case 2:
                    //角
                    $newPatMoney = bcadd($payMoney, 0, 0);
                    break;
                case 3:
                    //到角
                    $newPatMoney = round($payMoney, 1);
                    break;
                case 4:
                    //到元
                    $newPatMoney = round($payMoney, 0);
                    break;
                default:
                    $newPatMoney = 0;
                    break;
            }
            $rem_money = bcsub($payMoney, $newPatMoney, 2);
            $data['payMoney'] = $newPatMoney;
            $data['rem_money'] = $rem_money;
        } else {
            if ($isZero == StatusCode::$standard) {
                //手动抹零
                switch ($zeroSet['type']) {
                    case 1:
                        //分
                        $newPatMoney = bcadd($payMoney, 0, 1);
                        break;
                    case 2:
                        //角
                        $newPatMoney = bcadd($payMoney, 0, 0);
                        break;
                    case 3:
                        //到角
                        $newPatMoney = round($payMoney, 1);
                        break;
                    case 4:
                        //到元
                        $newPatMoney = round($payMoney, 0);
                        break;
                    default:
                        $newPatMoney = 0;
                        break;
                }
                $rem_money = bcsub($payMoney, $newPatMoney, 2);
                $data['payMoney'] = $newPatMoney;
                $data['rem_money'] = $rem_money;
            }
        }
        return ResultWrapper::success($data);
    }

    /**
     * Doc: (des="将购物车的数据进行分组 活动商品的限购级别以及价格，全部都基于此方法组装的数据")
     * User: XMing
     * Date: 2020/8/5
     * Time: 3:21 下午
     * @param array $data
     * @return ResultWrapper
     */
    private function cartGroupData(array $data)
    {
        if (empty($data)) {
            return ResultWrapper::success([]);
        }

        // 获取限购级别设置项
        $BasicSetupResult = $this->objMBasicSetup->getBasicField('limitLevel');
        if (!$BasicSetupResult->isSuccess()) {
            return ResultWrapper::fail($BasicSetupResult->getData(), $BasicSetupResult->getErrorCode());
        }
        $BasicSetup = $BasicSetupResult->getData();//之前的企业可鞥未设置此字段，给默认值5
        $limitLevel = isset($BasicSetup['limitLevel']) ? $BasicSetup['limitLevel'] : StatusCode::$standard;//限购级别

        $selectStateMap = [];
        //将同sku合并到一起s
        $dataMap = [];
        $skuBuyMap = [];
        foreach ($data as $datum) {
            $selectStateMap[$datum['skuId'] . ':' . $datum['activityId']] = $datum['selection'];
            $dataMap[$datum['skuId']] = [
                'id'           => $datum['id'],
                'goodsId'      => $datum['goodsId'],
                'goodsCode'    => $datum['goodsCode'],
                'shopId'       => $datum['shopId'],
                'source'       => $datum['source'],
                'goodsBasicId' => $datum['goodsBasicId'],
                'selection'    => $datum['selection'],
                'skuId'        => $datum['skuId'],
                'warehouseId'  => $datum['warehouseId'],
                'activityId'   => 0,
            ];
            $skuBuyMap[$datum['skuId']][] = $datum['buyNum'];
        }
        $this->selectStateMap = $selectStateMap;

        foreach ($dataMap as $skuId => $value) {
            $dataMap[$skuId]['buyNum'] = array_sum($skuBuyMap[$skuId]);
        }
        $data = array_values($dataMap);

        //获取客户信息
        $customer = $this->objDCustomer->get(['userCenterId' => $this->onlineUserId], 'id,type');
        if ($customer === false) {
            return ResultWrapper::fail($this->objDCustomer->error(), ErrorCode::$dberror);
        }
        if (empty($customer)) {
            $customer = [];
        }

        //获取商品是否是活动商品
        $allSkuIds = [];
        foreach ($data as $datum) {
            $allSkuIds[] = $datum['skuId'];
        }
        $activityResult = $this->objMActivity->getActivity([
            'skuIds'       => implode(',', $allSkuIds),
            'customerType' => isset($customer['type']) ? $customer['type'] : 0,
        ]);
        if (!$activityResult->isSuccess()) {
            return ResultWrapper::fail($activityResult->getData(), $activityResult->getErrorCode());
        }
        $activity = $activityResult->getData();
        $activityMap = [];
        foreach ($activity as $item) {
            $activityMap[$item['skuId']] = $item;
        }

        $update = [];
        foreach ($data as $item) {
            if (!isset($activityMap[$item['skuId']])) {
                //没有活动不用处理
                $update[] = self::buildData($item, $item['buyNum'], 0, $this->onlineUserId);
                continue;
            }
            //是活动商品
            //1。活动商品数量是否足够
            $len = $this->objActivityLimitCache->getLen($activityMap[$item['skuId']]['activityId'], $item['goodsId'], $item['skuId']);
            if ($len == 0) {
                //活动商品已购买完
                $update[] = self::buildData($item, $item['buyNum'], 0, $this->onlineUserId);
                continue;
            }
            //2。剩余数量
            //2.1 剩余数量足够
            if ($len >= $item['buyNum']) {
                //获取用户已购数量
                $userLimit = $this->objActivityLimitCache->getLimit($activityMap[$item['skuId']]['activityId'], $item['goodsId'], $item['skuId'], $this->onlineUserId);
                //3。用户限购
                switch ($limitLevel) {
                    case StatusCode::$standard:
                        if ($activityMap[$item['skuId']]['limitNum'] == 0) {
                            $update[] = self::buildData($item, $item['buyNum'], 0, $this->onlineUserId);
                            continue 2;
                        }
                        //3。1 活动期间限购
                        if ($userLimit > $activityMap[$item['skuId']]['limitNum']) {
                            //已经购买过活动商品了，剩下的都恢复原价
                            $update[] = self::buildData($item, $item['buyNum'], 0, $this->onlineUserId);
                            continue 2;
                        }
                        $allowBuyNum = $activityMap[$item['skuId']]['limitNum'] - $userLimit;
                        if ($allowBuyNum == 0) {
                            $update[] = self::buildData($item, $item['buyNum'], 0, $this->onlineUserId);
                            continue 2;
                        }

                        if ($item['buyNum'] <= $allowBuyNum) {
                            $update[] = self::buildData($item, $item['buyNum'], $activityMap[$item['skuId']]['activityId'], $this->onlineUserId);
                            continue 2;
                        }

                        if ($item['buyNum'] > $allowBuyNum) {
                            //分解
                            $update[] = self::buildData($item, $allowBuyNum, $activityMap[$item['skuId']]['activityId'], $this->onlineUserId);
                            $update[] = self::buildData($item, $item['buyNum'] - $allowBuyNum, 0, $this->onlineUserId);//原价不参与活动
                            continue 2;
                        }
                        break;
                    case StatusCode::$delete:
                        //3。2 单笔订单限购买
                        if ($activityMap[$item['skuId']]['limitNum'] < $item['buyNum']) {
                            //购车中的商品大于单笔限购，分解
                            $update[] = self::buildData($item, $activityMap[$item['skuId']]['limitNum'], $activityMap[$item['skuId']]['activityId'], $this->onlineUserId);
                            $update[] = self::buildData($item, $item['buyNum'] - $activityMap[$item['skuId']]['limitNum'], 0, $this->onlineUserId);
                            continue 2;
                        }

                        if ($activityMap[$item['skuId']]['limitNum'] >= $item['buyNum']) {
                            //都是活动价格
                            $update[] = self::buildData($item, $item['buyNum'], $activityMap[$item['skuId']]['activityId'], $this->onlineUserId);
                            continue 2;
                        }
                        break;
                }
            } else {
                //2。2 剩余数量不足
                $update[] = self::buildData($item, $item['buyNum'], 0, $this->onlineUserId);
                continue;
            }
        }

        if (empty($update)) {
            return ResultWrapper::fail('获取购物车数据失败', ErrorCode::$paramError);
        }

        $this->objDCart->beginTransaction();
        $deleteRes = $this->objDCart->delete(['userCenterId' => $this->onlineUserId]);
        if ($deleteRes === false) {
            $this->objDCart->rollBack();
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        $insertRes = $this->objDCart->insert($update, true);
        if ($insertRes === false) {
            $this->objDCart->rollBack();
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        $this->objDCart->commit();
        $where['userCenterId'] = $this->onlineUserId;
        $cartList = $this->objDCart->select($where);
        if ($cartList === false) {
            $this->objDCart->rollBack();
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success($cartList);
    }

    /**
     * Doc: (des="")
     * User: XMing
     * Date: 2020/8/5
     * Time: 5:22 下午
     * @param array $item
     * @param int $buyNum
     * @param int $activityId
     * @param int $userCenterId
     * @return array
     */
    private function buildData(array $item, int $buyNum, int $activityId, int $userCenterId)
    {
        $data = [
            'goodsId'      => $item['goodsId'],
            'goodsCode'    => $item['goodsCode'],
            'shopId'       => $item['shopId'],
            'source'       => $item['source'],
            'goodsBasicId' => $item['goodsBasicId'],
            'selection'    => isset($this->selectStateMap[$item['skuId'] . ':' . $activityId]) ? $this->selectStateMap[$item['skuId'] . ':' . $activityId] : StatusCode::$delete,
            'skuId'        => $item['skuId'],
            'warehouseId'  => $item['warehouseId'],
            'activityId'   => $activityId,
            'buyNum'       => $buyNum,
            'userCenterId' => $userCenterId
        ];
        return $data;
    }

    /**
     * app获取购物车内的数据
     * @return ResultWrapper
     * @throws \Exception
     */
    public function getCartByUserCenterId()
    {
        $result = $this->objDCart->select(['userCenterId' => $this->onlineUserId], 'id,goodsId,goodsCode,buyNum,shopId,source,goodsBasicId,selection,skuId,warehouseId,activityId', 'createTime DESC');
        if ($result === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        $result = self::formatMerge($result);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $dataResult = self::formatGoodsAndShop($result->getData());
        if (!$dataResult->isSuccess()) {
            return ResultWrapper::fail($dataResult->getData(), $dataResult->getErrorCode());
        }
        $data = $dataResult->getData();
        $return = [
            'data'  => $data,
            'total' => empty($data) ? 0 : count($data),
        ];
        return ResultWrapper::success($return);
    }

    /**
     * 后台获取购车数据
     * @return ResultWrapper
     * @throws \Exception
     */
    public function getManageCartByUserCenterId()
    {
        $result = $this->objDCart->select(['userCenterId' => $this->onlineUserId], 'id,goodsId,goodsCode,buyNum,shopId,goodsBasicId,selection,skuId,warehouseId,activityId', 'createTime DESC');
        if ($result === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        $dataResult = self::formatGoodsAndShop($result);
        if (!$dataResult->isSuccess()) {
            return ResultWrapper::fail($dataResult->getData(), $dataResult->getErrorCode());
        }
        $data = $dataResult->getData();
        $return = [
            'data'  => $data,
            'total' => empty($data) ? 0 : count($data),
        ];
        return ResultWrapper::success($return);
    }

    /**
     * 获取当前用户的购物车数据
     */
    private function getCartByUid()
    {
        $result = $this->objDCart->select(['userCenterId' => $this->onlineUserId], 'id,goodsId,goodsCode,buyNum,shopId,goodsBasicId,selection,skuId,warehouseId,activityId,cashierUid', 'createTime DESC');
        if ($result === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }

        return ResultWrapper::success($result);
    }

    /**
     * 更新选中状态
     * @param $params
     * @return ResultWrapper
     * @throws \Exception
     */
    public function updateSelection($params)
    {
        $dbResult = false;
        switch ($params['type']) {
            case self::$type['single']:
                if (empty($params['id'])) {
                    return ResultWrapper::fail('缺少cartId', ErrorCode::$paramError);
                }
                $dbResult = $this->objDCart->update(['selection' => $params['selection']], $params['id']);
                break;
            case self::$type['shop']:
                if (empty($params['shopId'])) {
                    return ResultWrapper::fail('缺少shopId', ErrorCode::$paramError);
                }
                $dbResult = $this->objDCart->update(['selection' => $params['selection']], ['shopId' => $params['shopId'], 'userCenterId' => $this->onlineUserId]);
                break;
            case self::$type['all']:
                $dbResult = $this->objDCart->update(['selection' => $params['selection']], ['userCenterId' => $this->onlineUserId]);
                break;
        }

        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success($dbResult);

    }

    /**
     * 删除购物车中商品(可批量)
     * @param $ids
     * @param boolean $order 是否是来自订单
     * @return ResultWrapper
     */
    public function delCart($ids, $order = false)
    {
        $dbResult = $this->objDCart->select($ids, 'id,goodsId,buyNum,skuId,activityId');
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        //限购减对应数据
        if ($order === true) {
            foreach ($dbResult as $goods) {
                if ($goods['activityId'] != 0) {
                    $userSurplusNum = $this->objActivityLimitCache->getLimit($goods['activityId'], $goods['goodsId'], $goods['skuId'], $this->onlineUserId);
                    $mapping[] = [
                        'activityId' => $goods['activityId'],
                        'goodsId'    => $goods['goodsId'],
                        'skuId'      => $goods['skuId'],
                        'buyNum'     => $userSurplusNum + $goods['buyNum'],
                    ];
                    self::userLimit($mapping);
                }
            }
        }
        $dbResult = $this->objDCart->delete($ids);
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success($dbResult);

    }

    /**
     * Doc: (des="判断字符串是否是一个整数")
     * User: XMing
     * Date: 2020/10/12
     * Time: 10:29 上午
     * @param $num
     * @return bool
     */
    private static function isInteger($num)
    {
        if ($num - floor($num) != 0) {
            return false;
        }
        return true;
    }

    /**
     * 拼装购物车数据(商铺->商品->规格->库存->价格->分组)
     * 此方法正在修改,勿动
     * @param $data
     * @return ResultWrapper
     * @throws \Exception
     */
    private function formatGoodsAndShop($data, $userCouponId = null, $vipCardId = null)
    {
        foreach ($data as &$value) {
            $value['buyNum'] = self::isInteger($value['buyNum']) ? (int)$value['buyNum'] : $value['buyNum'];
        }
        unset($value);
        $result = self::formatShop($data);//格式化店铺数据
        $result = self::formatGoods($result);//格式化商品数据
        if ($this->isFront === true) {
            $result = self::calExpress($result);
            if (!$result->isSuccess()) {
                return ResultWrapper::fail($result->getData(), $result->getErrorCode());
            }
            $result = $result->getData();
        }

        // 价格处理
        $priceResult = self::formatPrice($result);
        if (!$priceResult->isSuccess()) {
            return ResultWrapper::fail($priceResult->getData(), $priceResult->getErrorCode());
        }
        $result = $priceResult->getData();


        $inventoryResult = self::formatSkuInventory($result);//处理库存
        if (!$inventoryResult->isSuccess()){
            return ResultWrapper::fail($inventoryResult->getData(),$inventoryResult->getErrorCode());
        }
        $result = $inventoryResult->getData();
        $result = self::formatGroup($result, $userCouponId, $vipCardId);
        if(!$result->isSuccess()){
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * @param $data
     * @return ResultWrapper
     */
    private function formatMerge($data)
    {
        if (empty($data)) return ResultWrapper::success($data);
        foreach ($data as $goods) {
            if ($goods['activityId'] == 0) {
                continue;
            }
            //获取活动详情
            $activity = $this->objActivityLimitCache->getActivity($goods['activityId']);
            if ($activity['enableStatus'] == StatusCode::$delete
                || time() < $activity['startTime']
                || time() > $activity['endTime']) {
                //活动被下架,将此条数据合并到普通商品
                $cartResult = $this->objDCart->get(['goodsId' => $goods['goodsId'], 'shopId' => $goods['shopId'], 'skuId' => $goods['skuId'], 'activityId' => 0, 'userCenterId' => $this->onlineUserId]);
                if (empty($cartResult)) {
                    //addCart
                    $insert = [
                        'userCenterId' => $this->onlineUserId,
                        'selection'    => StatusCode::$standard,
                        'skuId'        => $goods['skuId'],
                        'goodsCode'    => $goods['goodsCode'],
                        'goodsId'      => $goods['goodsId'],
                        'shopId'       => $goods['shopId'],
                        'buyNum'       => $goods['buyNum'],//购买数量
                        'source'       => $goods['source'],
                        'goodsBasicId' => $goods['goodsBasicId'],
                        'warehouseId'  => $goods['warehouseId'],
                        'activityId'   => 0,//活动id
                    ];
                    $dbResult = $this->objDCart->insert($insert);
                } else {
                    //update
                    $dbResult = $this->objDCart->set_inc('buyNum', ['id' => $cartResult['id']], $goods['buyNum']);
                }
                $delResult = $this->objDCart->delete(['id' => $goods['id']]);
                //获取用户限购数量
                $userLimit = $this->objActivityLimitCache->getLimit($goods['activityId'], $goods['goodsId'], $goods['skuId'], $this->onlineUserId);
                $this->objActivityLimitCache->writeLimit($goods['activityId'], $goods['goodsId'], $goods['skuId'], $this->onlineUserId, $userLimit - $goods['buyNum']);
                if ($delResult === false) {
                    return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                }
                if ($dbResult === false) {
                    return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
                }
            }
        }
        $result = $this->objDCart->select(['userCenterId' => $this->onlineUserId], 'id,goodsId,goodsCode,buyNum,shopId,goodsBasicId,selection,skuId,warehouseId,activityId', 'createTime DESC');
        if ($result === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success($result);
    }


    /**
     * 获取店铺信息
     * (原数据上+店铺名称+店铺logo)
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    private function formatShop($data)
    {
        if (empty($data)) return $data;
        $objMGoods = new MShop($this->onlineEnterpriseId, $this->onlineUserId);
        $shopData = $objMGoods->getShopName(array_unique(array_column($data, 'shopId')));
        if (!empty($shopData)) {
            foreach ($data as &$shop) {
                $shop['shopName'] = isset($shopData[$shop['shopId']]['name']) ? $shopData[$shop['shopId']]['name'] : '';
                $shop['shopLogo'] = isset($shopData[$shop['shopId']]['logo']) ? $shopData[$shop['shopId']]['logo'] : '';
                $shop['cartId'] =getArrayItem($shop,'id',0);
            }
        }
        return $data;
    }

    /**
     * 商品信息
     * (原数据基础上+商品名称+商品图片)商品的状态 下架商品计入失效
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    private function formatGoods($data)
    {
        if (empty($data)) return $data;
        $objGoodsBasicRelevantCache = new GoodsBasicRelevant($this->onlineEnterpriseId);
        $objMGoods = new MGoods($this->onlineEnterpriseId, false, $this->onlineUserId);
        $goodsData = $objMGoods->getGoodsNames(array_unique(array_column($data, 'goodsId')));
        if (!empty($goodsData)) {
            $dbResult = $objMGoods->getNameByGoodsIds(array_values(array_unique(array_column($goodsData, 'basicGoodsId'))));
            if ($dbResult->isSuccess()) {
                $basicData = $dbResult->getData();
                foreach ($goodsData as &$goods) {
                    $goods['goodsName'] = isset($basicData[$goods['basicGoodsId']]['title']) ? $basicData[$goods['basicGoodsId']]['title'] : '';
                    $goods['describe'] = isset($basicData[$goods['basicGoodsId']]['describe']) ? $basicData[$goods['basicGoodsId']]['describe'] : '';
                    $goods['categoryId'] = isset($basicData[$goods['basicGoodsId']]['categoryId']) ? $basicData[$goods['basicGoodsId']]['categoryId'] : '';
                    $goods['brandId'] = isset($basicData[$goods['basicGoodsId']]['brandId']) ? $basicData[$goods['basicGoodsId']]['brandId'] : '';
                    $goods['categoryPath'] = isset($basicData[$goods['basicGoodsId']]['categoryPath']) ? $basicData[$goods['basicGoodsId']]['categoryPath'] : '';
                    $goods['specType'] = isset($basicData[$goods['basicGoodsId']]['specType']) ? $basicData[$goods['basicGoodsId']]['specType'] : StatusCode::$specType['single'];
                    $goods['storageCode'] = isset($basicData[$goods['basicGoodsId']]['storageCode']) ? $basicData[$goods['basicGoodsId']]['storageCode'] : '';
                    $goods['categoryName'] = empty($goods['categoryId']) ? '' : $objGoodsBasicRelevantCache->getNameByCategoryId($goods['categoryId']);
                    $goods['brandName'] = empty($goods['brandId']) ? '' : $objGoodsBasicRelevantCache->getNameByBrandId($goods['brandId']);
                    $goods['images'] = isset($basicData[$goods['basicGoodsId']]['images']) ? $basicData[$goods['basicGoodsId']]['images'] : [];
                    $goods['isEq'] = isset($basicData[$goods['basicGoodsId']]['isEq']) ? $basicData[$goods['basicGoodsId']]['isEq'] : StatusCode::$delete;
                }
            }
        }

        $objMSku = new MSku($this->onlineUserId, $this->onlineEnterpriseId);
        $specNameMapping = $objMSku->getSpecNameBySkuId(array_column($data, 'skuId'));
        if (!$specNameMapping->isSuccess()) {
            $specNameMapping = [];
        } else {
            $specNameMapping = $specNameMapping->getData();
        }

        foreach ($data as $key => &$val) {
            $val['goodsCode'] = createCode(StatusCode::$code['goodsBasic']['prefix'], $val['goodsBasicId'], StatusCode::$code['goodsBasic']['length']);
            $val['brandName'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['brandName'] : '';
            $val['categoryName'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['categoryName'] : '';
            $val['barCode'] = isset($specNameMapping[$val['skuId']]) ? $specNameMapping[$val['skuId']]['barCode'] : '';
            $val['storageCode'] = isset($goodsData[$val['goodsId']]['storageCode']) ? $goodsData[$val['goodsId']]['storageCode'] : '';
            $val['goodsName'] = isset($goodsData[$val['goodsId']]['goodsName']) ? $goodsData[$val['goodsId']]['goodsName'] : '';
            $images = isset($goodsData[$val['goodsId']]['images']) ? $goodsData[$val['goodsId']]['images'] : [];
            $val['goodsImages'] = empty($images) ? '' : $images[0];
            $val['isInvalid'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['enableStatus'] : StatusCode::$delete;//4=>失效商品
            $val['describe'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['describe'] : '';
            $val['categoryId'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['categoryId'] : '';
            $val['brandId'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['brandId'] : '';
            $val['categoryPath'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['categoryPath'] : '';
            $val['isActivity'] = empty($val['activityId']) ? StatusCode::$delete : StatusCode::$standard;//是否是活动商品
            $val['specType'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['specType'] : StatusCode::$specType['single'];
            $val['unitName'] = isset($specNameMapping[$val['skuId']]) ? $specNameMapping[$val['skuId']]['unitName'] : '';
            $val['specGroup'] = isset($specNameMapping[$val['skuId']]) ? $specNameMapping[$val['skuId']]['specGroup'] : [];
            $val['notExpress'] = StatusCode::$standard;
            $val['supplierId'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['supplierId'] : 0;
            $val['supplierName'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['supplierName'] : '';
            $val['isEq'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['isEq'] : StatusCode::$delete;
            $val['isDistribution'] = isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['isDistribution'] : StatusCode::$delete;

            $val['express'] = [
                'weight'      => isset($specNameMapping[$val['skuId']]) ? $specNameMapping[$val['skuId']]['weight'] : 0,
                'expressType' => isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['expressType'] : 0,
                'ruleId'      => isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['ruleId'] : 0,
                'expressFee'  => isset($goodsData[$val['goodsId']]) ? $goodsData[$val['goodsId']]['expressFee'] : 0,
            ];
            if ($val['isEq'] == StatusCode::$standard){
                $isMaster = isset($specNameMapping[$val['skuId']]) ? $specNameMapping[$val['skuId']]['isMaster'] : StatusCode::$delete;
                //超码商品
                if ($isMaster  == StatusCode::$delete){
                    $masterSkuResult = $this->objMSku->getMasterById($val['goodsBasicId']);
                    if ($masterSkuResult->isSuccess()){
                        $masterSku = $masterSkuResult->getData();
                        $conversion = isset($specNameMapping[$val['skuId']]) ? $specNameMapping[$val['skuId']]['conversion'] : 0;
                        $u = isset($masterSku['unitName']) ? $masterSku['unitName'] : '';
                        $u_1 = $val['unitName'];
                        $val['extends'] = ['uId' => $masterSku['id'], 'uId_1' => $val['skuId'], 'u' => $u, 'u_1' => $u_1, 'u_1_buy'=> $val['buyNum'],'conversion' => $conversion];
                    }
                }
            }
            if (isset($goodsData[$val['goodsId']]) && ($goodsData[$val['goodsId']]['enableStatus'] == StatusCode::$delete || $goodsData[$val['goodsId']]['deleteStatus'] == StatusCode::$delete)) {
                $val['invalidMsg'] = '此商品已下架';
                $this->invalidData[] = $val;//失效商品集合
                unset($data[$key]);
            }
        }
        return $data;
    }

    /**
     * 获取规格信息
     * (原数据+skuData+unitName)
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    private function formatSku($data)
    {
        if (empty($data)) return $data;
        $objMSku = new MSku($this->onlineUserId, $this->onlineEnterpriseId);
        $skuData = $objMSku->getSku(array_unique(array_column($data, 'skuId')));
        if (!empty($skuData)) {
            foreach ($data as &$goods) {
                $goods['skuData'][] = isset($skuData[$goods['skuId']]) ? $skuData[$goods['skuId']] : [];
                $goods['unitName'] = isset($skuData[$goods['skuId']]['unitName']) ? $skuData[$goods['skuId']]['unitName'] : '';
                $goods['conversion'] = isset($skuData[$goods['skuId']]['conversion']) ? $skuData[$goods['skuId']]['conversion'] : '';
            }
        }
        return $data;
    }

    /**
     * 查询库存以及成本(*goodsBasicId *warehouseId) 库存不足的商品计入失效
     * (原数据+costPrice+inventoryNum)
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    private function formatSkuInventory($data): ResultWrapper
    {
        $inventorySelectParams = [];
        $skuIds = [];
        foreach ($data as &$goods) {
            $skuIds[] = $goods['skuId'];
            $goods['inventory'] = 0;
            $inventorySelectParams[$goods['shopId']][] = $goods['skuId'];

        }
        unset($goods);

        $objMInventory = new MInventory($this->onlineEnterpriseId, $this->onlineUserId);
        $inventoryMap = [];
        foreach ($inventorySelectParams as $shopId => $skuIds){
            $inventoryResult = $objMInventory->getInventoryByShopIdAndSkuIds($shopId,$skuIds);
            if (!$inventoryResult->isSuccess()) {
                return ResultWrapper::fail($inventoryResult->getData(),$inventoryResult->getErrorCode());
            }
            $inventoryMap[$shopId] = $inventoryResult->getData();
        }

        //获取当前skuIds的主单位，及换算比例
        $skuConversionResult = $this->objMSku->getConversion($skuIds);
        if (!$skuConversionResult->isSuccess()) {
            return ResultWrapper::fail($skuConversionResult->getData(),$skuConversionResult->getErrorCode());
        }
        $skuConversionMapping = $skuConversionResult->getData();
        foreach ($data as $key => &$goods) {
            $thisSku = $skuConversionMapping[$goods['skuId']];
            if (!isset($inventoryMap[$goods['shopId']])){
                return ResultWrapper::fail('未获取到库存信息',ErrorCode::$paramError);
            }
            $row = $inventoryMap[$goods['shopId']];
            $goods['inventoryNum'] = isset($row[$goods['skuId']]['num']) ? $row[$goods['skuId']]['num'] : 0;
            $goods['costPrice'] = 0;
            $goods['conversion'] = $thisSku['conversion'];//换算比例
            //判断是否开启预售
            if ($goods['isDistribution'] == StatusCode::$delete) {
                //没有开启需要检测库存
                if ($goods['inventoryNum'] < $goods['buyNum']) {
                    $goods['isInvalid'] = StatusCode::$delete;
                    $goods['invalidMsg'] = '商品库存不足';
                    $this->invalidData[] = $goods;
                    unset($data[$key]);
                }
            }
        }
        return ResultWrapper::success($data);
    }

    /**
     * Doc: (des="计算运费")
     * User: XMing
     * Date: 2020/9/8
     * Time: 10:10 上午
     * @param array $data
     * @return ResultWrapper
     * @throws \Exception
     */
    private function calExpress(array $data)
    {

        if (empty($data)) return ResultWrapper::success($data);
        if (empty($this->onlineUserDefaultDeliveryType)) {
            return ResultWrapper::success($data);
        }
        //判断选择是否是快递配送方式
        if ($this->onlineUserDefaultDeliveryType != StatusCode::$deliveryType['goodsDelivery']) {
            return ResultWrapper::success($data);
        }
        $map = [];
        foreach ($data as $val) {
            $map[$val['shopId']][] = $val;
        }
        unset($data);
        //收货地址为空，按全国计算
        $data = [];
        foreach ($map as $shopId => $item) {
            $buildDataResult = self::buildExpressData($item, $shopId, $this->onlineUserAddressCode);
            if (!$buildDataResult->isSuccess()) {
                return ResultWrapper::fail($buildDataResult->getData(), $buildDataResult->getErrorCode());
            }
            $data = array_merge($data, $buildDataResult->getData());
        }

        return ResultWrapper::success($data);
    }

    /**
     * Doc: (des="运费计算规则")
     * User: XMing
     * Date: 2020/9/8
     * Time: 11:36 上午
     * @example https://www.xiaokeduo.com/help/11584.html
     * 1.按商品累加运费
     *   规则：分别计算出来商品使用模板的运费，和统一运费的最大值，再进行累加。
     *   1）不同或相同的商品，设置同一运费模板：按该模板设置的规则计算
     *   更新：不足续件数目的时候，仍然按照续件数目进行计算。
     *   例如商品A，B都是用模板M（首件10块，续2件5块），如果购买商品A和B，各一件，则一共购买两件，运费=10+5=15元。
     * @param array $data
     * @param string $code
     * @param int $shopId
     * @throws \Exception
     * @return ResultWrapper
     */
    private function buildExpressData(array $data, int $shopId, $code = null)
    {
        //提取设置了运费模版的模版规则id
        $allRuleIds = [];
        foreach ($data as $item) {
            if ($item['express']['expressType'] == StatusCode::$expressType['rule']) {
                $allRuleIds[] = $item['express']['ruleId'];
                continue;
            }
        }

        $allRuleIds = array_values(array_unique($allRuleIds));
        $roleMap = [];
        $objDDeliveryRule = new DDeliveryRule();
        $objDDeliveryRule->setTable('qianniao_delivery_rule_' . $this->onlineEnterpriseId);
        if (!empty($allRuleIds)) {
            $ruleLists = $objDDeliveryRule->select(['id' => $allRuleIds]);
            if ($ruleLists === false) {
                return ResultWrapper::fail($objDDeliveryRule->error(), ErrorCode::$dberror);
            }
            foreach ($ruleLists as $item) {
                $roleMap[$item['id']] = empty($item['setData']) ? [] : json_decode($item['setData'], true);
            }
        }

        // 查询基础设置-商品设置运费规则
        $objMBasicSetup = new MBasicSetup($this->onlineEnterpriseId);
        $BasicSetupResult = $objMBasicSetup->getBasicField('calculateExpressType');
        if (!$BasicSetupResult->isSuccess()) {
            return ResultWrapper::fail($BasicSetupResult->getData(), $BasicSetupResult->getErrorCode());
        }
        $BasicSetup = $BasicSetupResult->getData();
        $calculateExpressType = isset($BasicSetup['calculateExpressType']) ? $BasicSetup['calculateExpressType'] : StatusCode::$standard;
        switch ($calculateExpressType) {
            case StatusCode::$delete: // 累加运费规则
                $calResult = self::ruleAdd($data, $roleMap, $code, $shopId);
                break;
            case StatusCode::$standard:  // 组合运费规则
                $calResult = self::ruleGroup($data, $roleMap, $code, $shopId);
                break;
            default: // 默认使用组合运费规则
                $calResult = self::ruleGroup($data, $roleMap, $code, $shopId);
                break;
        }
        if (!$calResult->isSuccess()) {
            return ResultWrapper::fail($calResult->getData(), $calResult->getErrorCode());
        }
        $data = $calResult->getData();
        return ResultWrapper::success($data);
    }

    /**
     * Doc: (des="（规则一）按商品累加运费")
     * User: XMing
     * Date: 2020/9/25
     * Time: 9:14 上午
     * @param array $data
     * @param array $roleMap
     * @param string $code
     * @return ResultWrapper
     * @example https://www.xiaokeduo.com/help/11584.html
     *  1）不同或相同的商品，设置同一运费模板：按该模板设置的规则计算
     *      更新：不足续件数目的时候，仍然按照续件数目进行计算。
     *      例如商品A，B都是用模板M（首件10块，续2件5块），如果购买商品A和B，各一件，则一共购买两件，运费=10+5=15元。
     *  2）多种商品，分别设置不同金额的统一运费：以最高运费金额收取。
     *      更新：例如 商品A，B，C的统一运费分别为1元，2元和3元，一期购买这三个商品，则运费为3元。
     *  3）不同的商品，设置不同的运费模板：分别计算每个运费模板规则应收运费，再累加计算合计运费；
     *      例如： 例如商品A使用用模板M（首件，10块，续1件，5块），商品B使用模板N（首件，12块，续3件，5块），如果购买商品A和B，各2件，则运费=模板M的运费+模板N的运费=（10+5）+（12+5）=32元。
     *  4）统一运费商品，和运费模板商品一同结算：单独计算统一运费商品应收运费，再累加运费模板应收运费；
     *      例如：商品A和B使用统一运费，分别为2元和10元，商品C使用模板M（首1件，10块，续2件，5块），商品D使用模板N（首件，12块，续3件，5块），购买A，B，C和D各两件。
     *      此时统一运费（商品A和B）=10元；运费模板运费（商品C和D）=模板M的运费+模板N的运费=（10+5）+（12+5）=32元。
     *      则总运费=统一运费+运费模板运费=（10）+（32）=42元。
     */
    private function ruleAdd($data, $roleMap, $code, $shopId)
    {
        //初始化所有运费
        $freeExpressMoney = 0;//免邮费
        $ruleExpressMoney = 0;//模版规则运费
        $unifyExpressMoney = 0;//统一运费
        $ruleIdRelGoodsMap = [];//模版=>[商品数量，商品重量]
        foreach ($data as &$item) {
            //判断运费设置
            switch ($item['express']['expressType']) {
                case StatusCode::$expressType['free']:
                    //免邮费
                    $freeExpressMoney = bcadd($freeExpressMoney, 0, 2);
                    break;
                case StatusCode::$expressType['rule']:
                    $ruleId = $item['express']['ruleId'];
                    //运费模版
                    if (!isset($roleMap[$ruleId])) {
                        //运费模版规则不存在,不计算运费
                        continue 2;
                    }
                    if (!empty($code)) {
                        //地区
                        //先判断商品是否在配送区域
                        $notDispatchAreas = $roleMap[$ruleId]['not_dispatch_areas'];
                        $isExist = self::searchInArray($code, $notDispatchAreas);
                        if ($isExist === true) {
                            $item['notExpress'] = StatusCode::$delete;
                            continue 2;
                        }
                    }
                    $ruleIdRelGoodsMap[$ruleId]['num'] = (isset($ruleIdRelGoodsMap[$ruleId]['num']) ? $ruleIdRelGoodsMap[$ruleId]['num'] : 0) + $item['buyNum'];
                    $ruleIdRelGoodsMap[$ruleId]['weight'] = (isset($ruleIdRelGoodsMap[$ruleId]['weight']) ? $ruleIdRelGoodsMap[$ruleId]['weight'] : 0) + bcmul($item['express']['weight'], $item['buyNum'], 2);
                    break;
                case StatusCode::$expressType['unify']:
                    //统一运费,求其中最大值
                    if ($item['express']['expressFee'] > $unifyExpressMoney) {
                        //如果当前值大于目前的最大值，则使用当前的值覆盖
                        $unifyExpressMoney = $item['express']['expressFee'];
                    }
                    break;
                default:
                    //没有设置默认不计算运费
                    break;
            }
        }

        if (!empty($ruleIdRelGoodsMap)) {
            //计算模版运费
            foreach ($ruleIdRelGoodsMap as $ruleId => $value) {
                $type = $roleMap[$ruleId]['type'];//4:按件 5:按重量
                if (empty($code)) {
                    //没有地区code,使用全国统一运费的规则进行计算
                    $uniform = $roleMap[$ruleId]['uniform'];
                    $expressMoney = self::calculateExpress($type, $uniform, $value);
                    $ruleExpressMoney = bcadd($ruleExpressMoney, $expressMoney, 2);
                } else {
                    //匹配模版
                    $dispatchAreas = $roleMap[$ruleId]['areas'];
                    $searchRow = self::searchRowByCode($code, $dispatchAreas);
                    if ($searchRow === false) {
                        //当前地区没有匹配到,使用全国统一计算
                        $uniform = $roleMap[$ruleId]['uniform'];
                        $expressMoney = self::calculateExpress($type, $uniform, $value);
                        $ruleExpressMoney = bcadd($ruleExpressMoney, $expressMoney, 2);
                    } else {
                        //匹配到了规则
                        $rule = [
                            'first_num'    => $searchRow['first_num'],
                            'first_price'  => $searchRow['first_price'],
                            'second_num'   => $searchRow['second_num'],
                            'second_price' => $searchRow['second_price']
                        ];
                        $expressMoney = self::calculateExpress($type, $rule, $value);
                        $ruleExpressMoney = bcadd($ruleExpressMoney, $expressMoney, 2);
                    }
                }
            }
        }

        $this->expressShopMoney[$shopId] = bcadd($ruleExpressMoney, $unifyExpressMoney, 2);
        $this->expressMoney = bcadd($this->expressMoney, $this->expressShopMoney[$shopId], 2);

        return ResultWrapper::success($data);
    }

    /**
     * Doc: (des=" (规则二) 组合运费")
     * User: XMing
     * Date: 2020/9/25
     * Time: 12:03 下午
     * @param array $data
     * @param array $roleMap
     * @param string $code
     * @return ResultWrapper
     * @example https://www.xiaokeduo.com/help/11584.html
     * 规则：先将使用统一运费和运费模板的商品分开计算，再取二者较大的值，作为最终运费。
     *    1）计算使用运费模板的运费价格x:取所有商品中，首件金额最大的运费模板，计算使用该模板的所有商品运费；
     *        使用其他模板的所有商品都按照该商品所试用的续件金额来计算；
     *        最后再求和。
     *    2）计算使用统一运费的商品运费y：取最大的统一运费。
     *    3）比较x和y，运费=x和y的较大值。
     *    例如：
     *    1）不同或相同的商品，设置同一运费模板：同按商品累加运费的计算方式，按该模板设置的规则计算。
     *       例如商品A，B都是用模板M（首件10块，续2件5块），如果购买商品A和B，各一件，则一共购买两件，运费=10+5=15元。
     *    2）多种商品，分别设置不同金额的统一运费：同按商品累加运费的计算方式，以最高运费金额收取。
     *       例如 商品A，B，C的统一运费分别为1元，2元和3元，一起购买这三个商品，则运费为3元。
     *    3）不同的商品，设置不同的运费模板：不同于按照商品累加的计算方式。
     *       例如： 例如商品A（1件）使用用模板M（首1件，10块，续1件，5块），商品B（1件）使用模板N（首2件，12块，续1件，5块），如果购买商品A和B，各2件，则运费=模板N的运费（首费为12，大于模板M的10）+模板M的运费（按照续费计算）=（12）+（5*2）=22元。
     *       如果按商品累加计费，则运费=模板M的运费+模板N的运费=（10+5）+（12）=27元。
     *    4）统一运费商品，和运费模板商品一同结算：单独计算统一运费和运费模板，再取较大的数值作为最终运费；
     *       例如：商品A和B使用统一运费，分别为2元和10元，商品C使用模板M（首1件，10块，续2件，5块），商品D（1件）使用模板N（首2件，12块，续1件，5块），购买A，B，C和D各两件。
     *       此时统一运费（商品A和B）=10元；运费模板运费（商品C和D）=模板N的运费（首费为12，大于模板M的10）+模板M的运费（按照续费计算）=（12）+（5*2）=22元。总运费=统一运费和运费模板运费的较大值=22元。
     *       如果按商品累加计费，运费按照之前的计算=统一运费+运费模板运费=（10）+（27）=37元。
     */
    private function ruleGroup($data, $roleMap, $code, $shopId)
    {
        //初始化所有运费
        $freeExpressMoney = 0;//免邮费
        $ruleExpressMoney = 0;//模版规则运费
        $unifyExpressMoney = 0;//统一运费
        $ruleIdRelGoodsMap = [];//模版=>[商品数量，商品重量]
        foreach ($data as &$item) {
            //判断运费设置
            switch ($item['express']['expressType']) {
                case StatusCode::$expressType['free']:
                    //免邮费
                    $freeExpressMoney = bcadd($freeExpressMoney, 0, 2);
                    break;
                case StatusCode::$expressType['rule']:
                    $ruleId = $item['express']['ruleId'];
                    //运费模版
                    if (!isset($roleMap[$ruleId])) {
                        //运费模版规则不存在,不计算运费
                        continue 2;
                    }
                    if (!empty($code)) {
                        //地区
                        //先判断商品是否在配送区域
                        $notDispatchAreas = $roleMap[$ruleId]['not_dispatch_areas'];
                        $isExist = self::searchInArray($code, $notDispatchAreas);
                        if ($isExist === true) {
                            $item['notExpress'] = StatusCode::$delete;
                            continue 2;
                        }
                    }
                    $ruleIdRelGoodsMap[$ruleId]['num'] = (isset($ruleIdRelGoodsMap[$ruleId]['num']) ? $ruleIdRelGoodsMap[$ruleId]['num'] : 0) + $item['buyNum'];
                    $ruleIdRelGoodsMap[$ruleId]['weight'] = (isset($ruleIdRelGoodsMap[$ruleId]['weight']) ? $ruleIdRelGoodsMap[$ruleId]['weight'] : 0) + bcmul($item['express']['weight'], $item['buyNum'], 2);
                    break;
                case StatusCode::$expressType['unify']:
                    //统一运费,求其中最大值
                    if ($item['express']['expressFee'] > $unifyExpressMoney) {
                        //如果当前值大于目前的最大值，则使用当前的值覆盖
                        $unifyExpressMoney = $item['express']['expressFee'];
                    }
                    break;
                default:
                    //没有设置默认不计算运费
                    break;
            }
        }

        //选获取模版规则中首件的最高价格，然后减去首件的都按续件处理
        //唯一的区别，多个运费模版首费只算一次
        if (!empty($ruleIdRelGoodsMap)) {
            //计算模版运费
            foreach ($ruleIdRelGoodsMap as $ruleId => &$value) {
                $value['type'] = $roleMap[$ruleId]['type'];//4:按件 5:按重量
                if (empty($code)) {
                    //没有地区code,使用全国统一运费的规则进行计算
                    $uniform = $roleMap[$ruleId]['uniform'];
                    $value['rule'] = $uniform;
                } else {
                    //匹配模版
                    $dispatchAreas = $roleMap[$ruleId]['areas'];
                    $searchRow = self::searchRowByCode($code, $dispatchAreas);
                    if ($searchRow === false) {
                        //当前地区没有匹配到,使用全国统一计算
                        $uniform = $roleMap[$ruleId]['uniform'];
                        $value['rule'] = $uniform;
                    } else {
                        //匹配到了规则
                        $rule = [
                            'first_num'    => $searchRow['first_num'],
                            'first_price'  => $searchRow['first_price'],
                            'second_num'   => $searchRow['second_num'],
                            'second_price' => $searchRow['second_price']
                        ];
                        $value['rule'] = $rule;
                    }
                }
            }
            //使用组合运费计算规则，计算运费
            $ruleExpressMoney = self::calculateExpressGroup($ruleIdRelGoodsMap);
        }

        $this->expressShopMoney[$shopId] = bcadd($ruleExpressMoney, $unifyExpressMoney, 2);
        $this->expressMoney = bcadd($this->expressMoney, $this->expressShopMoney[$shopId], 2);

        return ResultWrapper::success($data);
    }

    /**
     * Doc: (des="以组合运费方式计算快递费用")
     * User: XMing
     * Date: 2020/9/25
     * Time: 2:44 下午
     * @param array $data
     * @return float
     */
    private static function calculateExpressGroup($data)
    {
        $ruleExpressMoney = 0;
        $max_first_price = 0;
        $max_rule_id = 0;
        foreach ($data as $ruleId => $item) {
            //获取首费用最高的
            if ($item['rule']['first_price'] > $max_first_price) {
                $max_first_price = $item['rule']['first_price'];
                $max_rule_id = $ruleId;
            }
        }
        unset($item);

        foreach ($data as $ruleId => &$item) {
            if ($ruleId != $max_rule_id) {
                continue;
            }
            //减去首重的件/重量
            switch ($item['type']) {
                case StatusCode::$delete:
                    //剩余续件的
                    $num = bcsub($item['num'], $item['rule']['first_num'], 2);
                    $item['num'] = ($num < 0) ? 0 : $num;
                    break;
                case StatusCode::$standard:
                    //剩余续重的
                    $weight = bcsub($item['weight'], $item['rule']['first_num'], 2);
                    $item['weight'] = ($weight < 0) ? 0 : $weight;
                    break;
            }
        }
        unset($item);

        //剩下的都是续
        foreach ($data as $ruleId => $item) {
            switch ($item['type']) {
                case StatusCode::$delete:
                    $mod = ($item['rule']['second_num'] == 0) ? 0 : ceil($item['num'] / $item['rule']['second_num']);
                    $expressMoney = bcmul($mod, $item['rule']['second_price'], 2);
                    $ruleExpressMoney = bcadd($ruleExpressMoney, $expressMoney, 2);
                    break;
                case StatusCode::$standard:
                    $mod = ($item['rule']['second_num'] == 0) ? 0 : ceil($item['weight'] / $item['rule']['second_num']);
                    $expressMoney = bcmul($mod, $item['rule']['second_price'], 2);
                    $ruleExpressMoney = bcadd($ruleExpressMoney, $expressMoney, 2);
                    break;
            }
            unset($expressMoney);
            unset($mod);
        }
        //最高首费用 + 各自模版续运费
        return bcadd($max_first_price, $ruleExpressMoney, 2);
    }


    /**
     * Doc: (des="根据模版规则，去计算运费")
     * User: XMing
     * Date: 2020/9/25
     * Time: 9:42 上午
     * @param int $type
     * @param array $rule
     * @param array $item
     * @return float
     */
    private static function calculateExpress($type, $rule, $item)
    {
        switch ($type) {
            case StatusCode::$delete:
                //按件
                //1.按件
                if ($item['num'] <= $rule['first_num']) return $rule['first_price'];
                $mod = ($rule['second_num'] == 0) ? 0 : ceil(($item['num'] - $rule['first_num']) / $rule['second_num']);
                return bcadd($rule['first_price'], bcmul($mod, $rule['second_price'], 2), 2);
                break;
            case StatusCode::$standard:
                //按重量
                $thisWeight = bcmul($item['weight'], $item['num'], 2);
                if ($thisWeight <= $rule['first_num']) return $rule['first_price'];
                $mod = ($rule['second_num'] == 0) ? 0 : ceil(($thisWeight - $rule['first_num']) / $rule['second_num']);
                return bcadd($rule['first_price'], bcmul($mod, $rule['second_price'], 2), 2);
                break;
            default:
                break;
        }
    }

    /**
     * Doc: (des="")
     * User: XMing
     * Date: 2020/9/8
     * Time: 3:29 下午
     * @param array $data
     * @param string $code
     * @return array|boolean
     */
    public function searchRowByCode($code, $data)
    {
        $arr = explode('-', $code);
        $provinceCode = isset($arr[0]) ? $arr[0] : 0;
        $cityCode = isset($arr[1]) ? $arr[1] : 0;
        $areaCode = isset($arr[2]) ? $arr[2] : 0;
        foreach ($data as $item) {
            $allProvinces = isset($item['provinces']) ? $item['provinces'] : [];
            $allCitys = isset($item['citys']) ? $item['citys'] : [];
            $allAreas = isset($item['areas']) ? $item['areas'] : [];
            //查看当前省是否存在,如果省不存在市肯定不存在
            if (!in_array($provinceCode, $allProvinces)) {
                continue;
            }
            //查看市是否存在，如果市不存在区肯定不存在
            if (!in_array($cityCode, $allCitys)) {
                continue;
            }
            if (!in_array($areaCode, $allAreas)) {
                continue;
            }
            //存在返回此条数据
            return $item;
        }
        return false;
    }

    /**
     * Doc: (des="查询某只是否在数组中")
     * User: XMing
     * Date: 2020/9/8
     * Time: 3:06 下午
     * @param array $data
     * @param string $code
     * @return boolean 不存在返回false 存在返回true
     */
    public function searchInArray($code, $data)
    {
        $arr = explode('-', $code);
        $provinceCode = isset($arr[0]) ? $arr[0] : 0;
        $cityCode = isset($arr[1]) ? $arr[1] : 0;
        $areaCode = isset($arr[2]) ? $arr[2] : 0;
        $allProvinces = isset($data['provinces']) ? $data['provinces'] : [];
        $allCitys = isset($data['citys']) ? $data['citys'] : [];
        $allAreas = isset($data['areas']) ? $data['areas'] : [];
        //查看当前省是否存在,如果省不存在市肯定不存在
        if (!in_array($provinceCode, $allProvinces)) {
            return false;
        }
        //查看市是否存在，如果市不存在区肯定不存在
        if (!in_array($cityCode, $allCitys)) {
            return false;
        }
        if (!in_array($areaCode, $allAreas)) {
            return false;
        }
        return true;
    }

    /**
     * 获取商品价格,及计算价格 ,(有促销活动的用促销价)
     * (原数据+price+originPrice+preferential+totalMoney)
     * @param $data
     * @return ResultWrapper
     * @throws \Exception
     */
    private function formatPrice($data)
    {
        if (empty($data)) return ResultWrapper::success($data);
        $objMPrice = new MPrice($this->onlineUserId, $this->onlineEnterpriseId);
        $objMActivity = new MActivity($this->onlineUserId, $this->onlineEnterpriseId);
        $objMCustomer = new MCustomer($this->onlineEnterpriseId, $this->onlineUserId);
        $selectParam['material'] = [];
        $allGoodsId = [];
        foreach ($data as &$goods) {
            $allGoodsId[] = $goods['goodsId'];
            $selectParam['material'][$goods['shopId']][] = $goods['goodsId'];
            $goods['originPrice'] = 0;//原价
            $goods['price'] = 0;//单价
            $goods['preferential'] = 0;//优惠差价
            $goods['totalMoney'] = 0;//小计(unitPrice*buyNum)
            $goods['activityMoney'] = 0;//互斥活动商品总金额
            $goods['isMutex'] = StatusCode::$delete;//初始化互斥状态

        }
        unset($goods);

        // 查询价格
        $selectParam['customerId'] = $this->customerId;
        $dbResult = $objMPrice->getPrice($selectParam);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), $dbResult->getErrorCode());
        }
        $priceResult = $dbResult->getData();
        unset($dbResult);

        $customerResult = $objMCustomer->getCustomerInfoByUserCenterId($this->onlineUserId);
        $activityArr = [];
        if ($customerResult->isSuccess()) {
            $customer = $customerResult->getData();
            //获取促销活动价格
            $dbResult = $objMActivity->getActivity([
                'goodsId'      => implode(',', $allGoodsId),
                'customerType' => isset($customer['type']) ? $customer['type'] : 0,
            ]);//TODO
            $activityResult = $dbResult->getData();
            unset($dbResult);
            foreach ($activityResult as $activity) {
                $activityArr[$activity['goodsId'] . $activity['skuId']] = $activity;
            }
        }

        //商品价格信息
        $goodsArr = [];
        foreach ($priceResult as $shopPriceArr) {
            foreach ($shopPriceArr as $goodsId => $goodsSkuArr) {
                $goodsArr[$goodsId] = $goodsSkuArr;
            }
        }

        // 查询当前用户是否购买过vip会员卡
        $enableMemberPrice = false;
        $objMVipCard = new MVipCard($this->onlineEnterpriseId, $this->onlineUserId, true);
        $dbResult = $objMVipCard->getAllVipCardByCustomerId($this->customerId);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), ErrorCode::$dberror);
        }
        $vipCard = $dbResult->getData();
        if (!empty($vipCard)){
            foreach ($vipCard as $key => $value){
                if($value['memberSpecialPrice'] == StatusCode::$standard){
                    $enableMemberPrice = true;
                }
            }
        }

        foreach ($data as &$goods) {
            //此商品存在促销价
            if (isset($activityArr[$goods['goodsId'] . $goods['skuId']])) {
                $price = $goodsArr[$goods['goodsId']][$goods['skuId']]['salePrice'];//商品正常销售价格
                //从缓存中获取活动详情
                if (isset($goods['activityId']) && !empty($goods['activityId'])) {
                    $price = $activityArr[$goods['goodsId'] . $goods['skuId']]['price'];//商品活动价格
                    $activityDetail = $this->objActivityLimitCache->getActivity($goods['activityId']);
                    if (!empty($activityDetail) && $activityDetail['isMutex'] == StatusCode::$standard) {
                        //开启互斥商品的总金额
                        $goods['activityMoney'] = bcmul($price, $goods['buyNum'], 2);
                        $goods['isMutex'] = StatusCode::$standard;
                    }
                }
            } else {
                if (isset($goodsArr[$goods['goodsId']][$goods['skuId']])) {
                    $priceInfo = $goodsArr[$goods['goodsId']][$goods['skuId']];//此商品规格的价格信息
                    $price = isset($priceInfo['salePrice']) ? $priceInfo['salePrice'] : 0;

                    // 会员价  启用会员价不生效阶梯价了
                    $memberPrice = getArrayItem($priceInfo, 'memberPrice', 0);
                    if($enableMemberPrice && $memberPrice){
                        $price = $memberPrice;
                    }else{
                        // 开启阶梯价
                        if ($priceInfo['enabledLadder']) {
                            foreach ($priceInfo['ladderPrice'] as $ladder) {
                                $ladder['to'] = empty($ladder['to']) ? 9999 : $ladder['to'];
                                if ($goods['buyNum'] >= $ladder['from'] && $goods['buyNum'] <= $ladder['to']) {
                                    $price = isset($ladder['price']) ? $ladder['price'] : 0;
                                    continue;
                                }
                            }
                        }
                    }
                }
            }
            if (!isset($price)) $price = 0;
            $price = floatval($price);
            $goods['price'] = sprintf("%.2f", $price);
            $goods['originPrice'] = sprintf("%.2f", $price);//没有优惠活动暂时原价=销售价
            $goods['preferential'] = 0;//优惠券优惠金额
            $goods['totalMoney'] = bcmul($price, $goods['buyNum'], 2);//商品小计金额
            $goods['vipDiscount'] = 0;//会员卡优惠金额
            $goods['setNum'] = isset( $goodsArr[$goods['goodsId']][$goods['skuId']]['setNum'] ) ? $goodsArr[$goods['goodsId']][$goods['skuId']]['setNum'] : 0;//起定数量
            if (empty($goods['totalMoney'])) {
                return ResultWrapper::fail('计算商品价格时出现异常', ErrorCode::$paramError);
            }
        }
        return ResultWrapper::success($data);
    }

    /**
     * 数据分组
     *
     * @param $data
     * @param null $userCouponId
     * @param null $vipCardId
     * @return ResultWrapper
     */
    private function formatGroup($data, $userCouponId = null, $vipCardId = null)
    {
        static $checkNum = 0;//购物车中选中商品数量
        static $totalMoney = 0;//原总额
        static $payMoney = 0;//实际支付金额
        static $preferential = 0;//优惠券优惠
        static $cartNum = 0;
        static $goodsNum = 0;//购物车商品数量
        static $vipDiscount = 0;//会员卡优惠金额
        static $vipDoubleDiscount = 0;//会员卡折上折优惠金额
        static $activityMoney = 0;//互斥活动商品总金额
        static $expressMoney = 0;//运费
        $goodsData = [];

        foreach ($data as $key => $val) {
            if (!isset($val['expressMoney'])) {
                $val['expressMoney'] = '0.00';
            }
            $shopGoodsData[$val['shopId']][] = $val;

            if (!isset($goodsData[$val['shopId']]['totalMoney'])) {
                $goodsData[$val['shopId']]['totalMoney'] = '0.00';
            }
            if (!isset($goodsData[$val['shopId']]['preferential'])) {
                $goodsData[$val['shopId']]['preferential'] = '0.00';
            }

            if (!isset($goodsData[$val['shopId']]['expressMoney'])) {
                $goodsData[$val['shopId']]['expressMoney'] = '0.00';
            }
            $goodsData[$val['shopId']] = [
                'shopId'        => $val['shopId'],
                'shopName'      => $val['shopName'],
                'shopLogo'      => $val['shopLogo'],
                'expressMoney'  => isset($this->expressShopMoney[$val['shopId']]) ? $this->expressShopMoney[$val['shopId']] : 0,
                'totalMoney'    => bcadd($goodsData[$val['shopId']]['totalMoney'], $val['totalMoney'], 2),//(购物车总金额)
                'preferential'  => bcadd($goodsData[$val['shopId']]['preferential'], bcmul($val['preferential'], $val['buyNum']), 2),//店铺总优惠金额
                'payMoney'      => bcadd((isset($this->expressShopMoney[$val['shopId']]) ? $this->expressShopMoney[$val['shopId']] : 0), bcsub(bcadd($goodsData[$val['shopId']]['totalMoney'], $val['totalMoney']), bcadd($goodsData[$val['shopId']]['preferential'], bcmul($val['preferential'], $val['buyNum'])), 2), 2),//(购物实付金额)
                'shopGoodsData' => $shopGoodsData[$val['shopId']],//商品数据
            ];
            //统计选中商品总数
            if ($val['selection'] == StatusCode::$standard) {
                $checkNum = bcadd($checkNum, 1);
                $totalMoney = bcadd($totalMoney, $val['totalMoney'], 2);
                $payMoney = bcadd(0, bcadd($payMoney, bcsub($val['totalMoney'], bcmul($val['buyNum'], $val['preferential'])), 2), 2);
                $preferential = bcadd($preferential, bcmul($val['buyNum'], $val['preferential']), 2);
                $activityMoney = bcadd($activityMoney, $val['activityMoney'], 2);//互斥商品总金额
            }
            $cartNum = bcadd($cartNum, 1);
            $goodsNum = bcadd($goodsNum, $val['buyNum']);
        }

        $result = [
            'totalMoney'        => $totalMoney,
            'payMoney'          => $payMoney,
            'preferential'      => $preferential,
            'vipDiscount'       => $vipDiscount,
            'vipDoubleDiscount' => $vipDoubleDiscount,
            'activityMoney'     => $activityMoney,
            'expressMoney'      => $this->expressMoney,
            'checkNum'          => (int)$checkNum,
            'cartNum'           => (int)$cartNum,
            'goodsNum'          => (int)$goodsNum,
            'goodsData'         => array_values($goodsData),
            'invalidData'       => array_values($this->invalidData)
        ];

        $objMPreferentialProcess = new MPreferentialProcess($this->onlineUserId, $this->onlineEnterpriseId);
        $result = $objMPreferentialProcess->Preferential($result, $vipCardId, $userCouponId);
        if(!$result->isSuccess()){
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $result = $result->getData();

        if (
            (!empty($vipCardId) && $vipCardId != 0)
            || (!empty($userCouponId) && $userCouponId != 0)
        ) {
            //优惠券优惠,会员卡优惠分摊到商品
            $result = self::calAvg($result);
        }

        //处理运费
        $result = self::freeExpressPrice($result);
        $result['expressMoney'] != 0 && $result['payMoney'] = bcadd($result['payMoney'], $result['expressMoney'], 2);
        return ResultWrapper::success($result);
    }

    /**
     * Doc: (des="")
     * User: XMing
     * Date: 2020/9/18
     * Time: 2:56 下午
     * @param $result
     * @return mixed
     */
    private function freeExpressPrice($result)
    {
        $expressMoney = $result['expressMoney'];
        $payMoney = $result['payMoney'];
        $goodsData = $result['goodsData'];
        if ($expressMoney != 0 && $this->freeExpressPrice <= $payMoney && $this->freeExpressPrice != 0) {
            foreach ($goodsData as &$datum) {
                $shopExpressMoney = $datum['expressMoney'];
                $datum['expressMoney'] = '0.00';
                $datum['payMoney'] = bcsub($datum['payMoney'], $shopExpressMoney, 2);
            }
            $expressMoney = '0.00';
        }
        $result['goodsData'] = $goodsData;
        $result['expressMoney'] = $expressMoney;
        return $result;
    }

    /**
     *
     * 优惠计算分摊规则
     * A商品 100元
     * B商品 300元
     * 商品总价 400元
     *
     * A商品享受的优惠 = 优惠券金额*(A商品原价/商品总价)
     * B商品享受的优惠 = 优惠券金额*(B商品原价/商品总价)
     *
     * 优惠券(满100减20)        -20
     * 共                      -220
     * 实际付款                 180
     * A总(220*(100/400)=55)  优惠券(20*(100/400)=5)
     * B总(220*(300/400)=165) 优惠券(20*(300/400)=15)
     * @param $data
     * @return mixed
     */
    private function calAvg($data)
    {
        $account = 0; // 优惠劵优惠金额
        foreach ($data['goodsData'] as &$shops) {
            $count = count($shops['shopGoodsData']);
            foreach ($shops['shopGoodsData'] as $key => &$goods) {
                // 是否为互斥商品
                if ($goods['isMutex'] == StatusCode::$delete) {
                    //活动商品不分摊
                    if ($data['preferential'] != 0) {
                        //有优惠券优惠,分摊金额
                        if ($key + 1 != $count) {
                            $goods['preferential'] = bcmul($data['preferential'], bcdiv($goods['totalMoney'], $data['totalMoney'], 2), 2);
                            $account = bcadd($account, $goods['preferential'], 2);
                        } else {
                            //最后一个商品分摊金额=总优惠-其他均摊
                            $goods['preferential'] = bcsub($data['preferential'], $account, 2);
                        }
                    }
                }
                if ($data['vipDiscount'] != 0) {
                    /**
                     * A商品 10 B商品 20 合计30元 折扣1折 A打折9 B打折18 总折扣27
                     * A商品折扣金额 = 10 - (10 X (30 - 27)) / 30
                     */

                    // 会员卡总优惠金额 = 会员卡优惠后的总金额 = 订单实际支付金额
                    $vipCardEndDiscountMoney = bcsub($data['totalMoney'],$data['vipDiscount'], 2);
                    // 当前商品优惠后的金额 = 当前商品价格 * 会员卡优惠后总金额 / 订单实际支付金额
                    $goodsVipCardEndDiscountMoney = bcdiv(bcmul($goods['totalMoney'],$vipCardEndDiscountMoney,2), $data['totalMoney'],2);
                    // 当前商品优惠金额 = 当前商品价格 - 当前商品优惠后的金额
                    $goods['vipDiscount'] = bcsub($goods['totalMoney'], $goodsVipCardEndDiscountMoney, 2);
                }
            }
        }
        return $data;
    }

    /**
     * 后台--清空用户购物车
     */
    public function clearCart()
    {
        $where['userCenterId'] = $this->onlineUserId;
        $dbResult = $this->objDCart->delete($where);
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success($dbResult);
    }

    /**
     * 批量删除购物车商品
     * @param $params
     * @return ResultWrapper
     */
    public function clearCartByGoodsId($params)
    {
        $where['userCenterId'] = $this->onlineUserId;
        $where['goodsId'] = $params['goodsId'];
        $dbResult = $this->objDCart->delete($where);
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success($dbResult);
    }

    /**
     * 确认订单
     *
     * @param int $userCouponId 优惠券id
     * @param string $vipCardId 会员卡id
     * @param string $addressId
     * @param string $deliveryId
     * @return ResultWrapper
     * @throws \Exception
     */
    public function confirmationOfOrder($userCouponId = '', $vipCardId = '', $addressId = '', $deliveryId = '')
    {
        // 获取客户信息
        $objMCustomer = new MCustomer($this->onlineEnterpriseId, $this->onlineUserId);
        $result = $objMCustomer->getCustomerData(['userCenterId'=>$this->onlineUserId],'*', true);
        if(!$result->isSuccess()){
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $customerData = $result->getData();
        if (empty($customerData)) {
            return ResultWrapper::fail('未找到客户信息', ErrorCode::$paramError);
        }
        $customerId = $customerData['id'];

        // 获取客户的默认收货地址
        $objMShippingAddress = new MShippingAddress($this->onlineEnterpriseId);
        $selectParams = [
            'deleteStatus'  => StatusCode::$standard,
            'defaultStatus' => StatusCode::$standard,
            'customerId'    => $customerId
        ];
        $dbResult = $objMShippingAddress->getShippingAddressInfo($selectParams);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), ErrorCode::$dberror);
        }
        $addRessData = $dbResult->getData();
        unset($dbResult);

        // 获取自提点
        $objMDeliverySetting = new MDeliverySetting($this->onlineUserId, $this->onlineEnterpriseId);
        $selfExpressRuleResult = $objMDeliverySetting->getAllSelfExpressRule();
        if (!$selfExpressRuleResult->isSuccess()) {
            return ResultWrapper::fail($selfExpressRuleResult->getData(), $selfExpressRuleResult->getErrorCode());
        }
        $selfExpressRule = $selfExpressRuleResult->getData();

        //支付方式
        $objMEnterpriseBindPayment = new MEnterpriseBindPayment($this->onlineUserId, $this->onlineEnterpriseId);
        $paymentData = $objMEnterpriseBindPayment->getPaymentType();
        if (!$paymentData->isSuccess()) {
            $paymentData = [];
        } else {
            $paymentData = $paymentData->getData();
        }
        //配送方式
        $objMDeliverySetting = new MDeliverySetting($this->onlineUserId, $this->onlineEnterpriseId);
        $DeliveryData = $objMDeliverySetting->allDelivery();
        if (!$DeliveryData->isSuccess()) {
            $DeliveryData = [];
        } else {
            $DeliveryData = array_values($DeliveryData->getData()['data']);
        }

        //当前用户选择的配送方式
        if (!empty($deliveryId)) {
            $this->onlineUserDefaultDeliveryType = $deliveryId;
        } else {
            if (!empty($DeliveryData)) {
                foreach ($DeliveryData as $value) {
                    if ($value['defaultStatus'] == StatusCode::$standard) {
                        $this->onlineUserDefaultDeliveryType = $value['deliveryType'];
                        break;
                    }
                }
            }
        }

        //当前用户的收货地址code 省-市-区
        if (!empty($addressId)) {
            $addressResult = $objMShippingAddress->getShippingAddressInfo(['id' => $addressId]);
            if (!$addressResult->isSuccess()) {
                return ResultWrapper::fail($addressResult->getData(), ErrorCode::$dberror);
            }
            $addressInfo = $addressResult->getData();
            if (!empty($addressInfo)) {
                $this->onlineUserAddressCode = $addressInfo['provinceCode'] . '-' . $addressInfo['cityCode'] . '-' . $addressInfo['districtCode'];
            }
        } else {
            if (!empty($addRessData)) {
                $this->onlineUserAddressCode = $addRessData['provinceCode'] . '-' . $addRessData['cityCode'] . '-' . $addRessData['districtCode'];
            }
        }

        //获取当前企业设置的包邮价格
        $objMBasicSetup = new MBasicSetup($this->onlineEnterpriseId);
        $freeExpressPriceResult = $objMBasicSetup->getBasicField('freeExpressPrice');
        if (!$freeExpressPriceResult->isSuccess()) {
            return ResultWrapper::fail($freeExpressPriceResult->getData(), $freeExpressPriceResult->getData());
        }
        $freeExpressPrice = $freeExpressPriceResult->getData();
        $this->freeExpressPrice = (isset($freeExpressPrice['freeExpressPrice']) && !empty($freeExpressPrice['freeExpressPrice'])) ? $freeExpressPrice['freeExpressPrice'] : 0;

        //获取购物车选中的数据
        $dbResult = $this->objDCart->select(['selection' => StatusCode::$standard, 'userCenterId' => $this->onlineUserId], 'id,goodsId,goodsCode,buyNum,shopId,goodsBasicId,selection,skuId,warehouseId,activityId', 'createTime DESC');
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }

        if (empty($dbResult)) {
            return ResultWrapper::fail('购物车中没有选定的商品', ErrorCode::$paramError);
        }

        //所有结算商品id
        $goodsIds = array_values(array_column($dbResult, 'goodsId'));

        $cartDataResult = self::formatGoodsAndShop($dbResult, $userCouponId, $vipCardId);
        if (!$cartDataResult->isSuccess()) {
            return ResultWrapper::fail($cartDataResult->getData(), $cartDataResult->getErrorCode());
        }
        $cartData = $cartDataResult->getData();
        if (empty($cartData['goodsData'])) {
            return ResultWrapper::fail('购物车中商品已失效,不能进行结算', ErrorCode::$paramError);
        }


        if (empty($addRessData)) {
            $cartData['address'] = (object)[];
        } else {
            $objMSysAreaChina = new MSysAreaChina();
            $areaName = $objMSysAreaChina->getNameByCode([
                $addRessData['provinceCode'],
                $addRessData['cityCode'],
                $addRessData['districtCode']
            ]);
            $cartData['address'] = [
                'name'         => isset($addRessData['name']) ? $addRessData['name'] : '',
                'mobile'       => isset($addRessData['mobile']) ? $addRessData['mobile'] : '',
                'address'      => isset($addRessData['address']) ? $addRessData['address'] : '',
                'id'           => isset($addRessData['id']) ? $addRessData['id'] : '',
                'provinceName' => isset($areaName[$addRessData['provinceCode']]) ? $areaName[$addRessData['provinceCode']] : '',
                'cityName'     => isset($areaName[$addRessData['cityCode']]) ? $areaName[$addRessData['cityCode']] : '',
                'districtName' => isset($areaName[$addRessData['districtCode']]) ? $areaName[$addRessData['districtCode']] : '',
            ];
        }

        //订单使用的优惠券
        $userCouponInfo = [];
        if (!empty($userCouponId)) {
            $objMUserCoupon = new MUserCoupon($this->onlineUserId, $this->onlineEnterpriseId);
            $dbResult = $objMUserCoupon->getUserCoupon(['id' => $userCouponId]);
            if ($dbResult->isSuccess()) $userCouponInfo = $dbResult->getData();
            $userCouponInfo = isset($userCouponInfo[0]) ? $userCouponInfo[0] : [];
        }

        $cartData['useCoupon'] = $userCouponInfo;

        // 订单金额小于银行打款设置的启用金额,不支持银行打款
        if(!empty($paymentData)){
            foreach ($paymentData as $key => $value){
                if($value['id'] == StatusCode::$payType['bankLoans'] && $cartData['payMoney'] < $value['limit']){
                    unset($paymentData[$key]);
                }
            }
        }

        $cartData['payment'] = array_values($paymentData);
        $cartData['delivery'] = $DeliveryData;
        $cartData['selfExpressRule'] = $selfExpressRule;
        //优惠券数据
        $cartData = self::findCoupon($cartData);

        //获取订单会员卡
        $vipCard = self::getVipCard($goodsIds);
        if ($vipCard->isSuccess()) {
            $vipCard = $vipCard->getData();
        } else {
            $vipCard = [];
        }
        $cartData['vipCard'][] = $vipCard;

        // 客户余额
        $cartData['balance'] = $customerData['money'];

        // 会员余额
        $cartData['memberBalance'] = $customerData['memberBalance'];
        return ResultWrapper::success($cartData);
    }

    /**
     * @param $goodsIds
     * @return array|ResultWrapper
     */
    public function getVipCard($goodsIds)
    {
        $objMVipCard = new MVipCard($this->onlineEnterpriseId, $this->onlineUserId, true);
        $pageParams = pageToOffset(1, 10);
        $selectParams['limit'] = $pageParams['limit'];
        $selectParams['offset'] = $pageParams['offset'];
        $dbResult = $objMVipCard->getMyVipCards($selectParams);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), ErrorCode::$dberror);
        }
        $vipCard = $dbResult->getData()['data'];
        if (empty($vipCard)) return ResultWrapper::success([]);

        if (!empty($goodsIds)) {
            foreach ($vipCard as $key => $card) {
                if ($card['mode'] == StatusCode::$delete) {
                    $cardGoodsArr = explode(',', $card['goodsIds']);
                    foreach ($goodsIds as $goodsId) {
                        if (!in_array($goodsId, $cardGoodsArr)) {
                            unset($vipCard[$key]);
                        }
                    }
                }
            }
        }
        $vipCard = empty($vipCard) ? [] : array_shift($vipCard);
        return ResultWrapper::success($vipCard);
    }

    /**
     * 判断在使用范围的店铺,指定商品是否满足最小金额
     * 查询可用的优惠券
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    private function findCoupon($data)
    {
        $objMUserCoupon = new MUserCoupon($this->onlineUserId, $this->onlineEnterpriseId);
        $objGoodsBasicRelevant = new GoodsBasicRelevant($this->onlineEnterpriseId);
        $payAmount = $data['payMoney'];
        if ($data['activityMoney'] != 0) {
            $payAmount = bcsub($payAmount, $data['activityMoney'], 2);
        }
        $dbResult = $objMUserCoupon->availableCoupon([
            'payAmount' => $payAmount
        ]);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), ErrorCode::$dberror);
        }

        $coupon = $dbResult->getData();
        foreach ($coupon as $key => $val) {
            $useShop = explode(',', $val['useShop']);//此优惠券可以使用的店铺
            $applyRange = $val['applyRange'];//优惠券使用范围
            $cart[$val['id']]['money'] = 0;
            foreach ($data['goodsData'] as $shopGoodsData) {
                foreach ($shopGoodsData['shopGoodsData'] as $goodsDetail) {
                    $categoryId = $objGoodsBasicRelevant->getNameByBasicId($goodsDetail['goodsBasicId'], 'categoryId');
                    $brandId = $objGoodsBasicRelevant->getNameByBasicId($goodsDetail['goodsBasicId'], 'brandId');
                    if (in_array($shopGoodsData['shopId'], $useShop) &&
                        ($applyRange == StatusCode::$applyRange['allGoods'] ||
                            in_array($categoryId, explode(',', $val['categoryCollect'])) ||
                            in_array($goodsDetail['goodsId'], explode(',', $val['goodsCollect'])) ||
                            in_array($brandId, explode(',', $val['brandCollect'])))
                    ) {
                        $cart[$val['id']]['money'] = bcadd($cart[$val['id']]['money'], $goodsDetail['totalMoney']);//规定范围内的商品总金额
                    }
                }
            }
            if ($val['minPrice'] != 0 && $cart[$val['id']]['money'] < $val['minPrice']) {
                unset($coupon[$key]);//不符合要求
            }
        }

        $data['coupon'] = array_values($coupon);
        return $data;
    }

    /**
     * 获取商品,规格 购物车数量
     * @param $goodsIds
     * @return array
     */
    public function getCartNumByGoodsId($goodsIds)
    {
        $cartData = $this->objDCart->select(['goodsId' => $goodsIds, 'userCenterId' => $this->onlineUserId], 'buyNum,goodsId,skuId');
        if ($cartData === false) {
            return [];
        }

        $return = [];
        foreach ($cartData as $key => $val) {
            $return[$val['goodsId']][$val['skuId']] = $val['buyNum'];
        }

        return $return;
    }

    /**
     * 商品列表页调整购买数量
     * @param $params
     * @return ResultWrapper
     * @throws \Exception
     */
    public function updateCartNum($params)
    {
        if ($params['buyNum'] == 0) {
            $dbResult = $this->objDCart->delete([
                'goodsId'      => $params['goodsId'],
                'skuId'        => $params['skuId'],
                'userCenterId' => $this->onlineUserId
            ]);
        } else {
            $goods = self::getCartInventory([
                'goodsId'      => $params['goodsId'],
                'skuId'        => $params['skuId'],
                'userCenterId' => $this->onlineUserId
            ]);
            if (!$goods->isSuccess()) {
                return ResultWrapper::fail($goods->getData(), ErrorCode::$dberror);
            }
            $goods = $goods->getData();
            $inventoryNum = isset($goods[0]['inventory']) ? $goods[0]['inventory'] : 0;//商品剩余库存
            $setNum = isset($goods[0]['setNum']) ? $goods[0]['setNum'] : 0;//商品规格起订数量
            $title = isset($goods[0]['title']) ? $goods[0]['title'] : '';//商品名称
            if ($inventoryNum < $params['buyNum']) {
                return ResultWrapper::fail('商品库存不足', ErrorCode::$paramError);
            }
            if ($params['buyNum'] < $setNum) {
                return ResultWrapper::fail($title . '最小起订数量' . $setNum, ErrorCode::$paramError);
            }
            $dbResult = $this->objDCart->update(
                [
                    'buyNum' => $params['buyNum']
                ],
                [
                    'goodsId'      => $params['goodsId'],
                    'skuId'        => $params['skuId'],
                    'userCenterId' => $this->onlineUserId
                ]
            );
        }
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        return ResultWrapper::success($dbResult);
    }

    /**
     * 验证购物车内商品规格的库存
     * @param $selectParams
     * @return int|ResultWrapper
     * @throws \Exception
     */
    private function getCartInventory($selectParams)
    {
        $dbResult = $this->objDCart->get($selectParams);
        if ($dbResult === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        if (empty($dbResult)) {
            return ResultWrapper::success([]);
        }
        $result = self::checkCart([$dbResult]);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * Doc: (des="")
     * User: XMing
     * Date: 2021/1/8
     * Time: 10:30 上午
     * @param int $skuId
     * @param int $userId
     * @return ResultWrapper
     */
    public function getCartBuyNumBySku(int $skuId, int $userId): ResultWrapper
    {
        $sql = 'SELECT SUM(`buyNum`) AS subBuyNum FROM qianniao_cart_' . $this->onlineEnterpriseId . ' WHERE skuId = ' . $skuId . ' AND userCenterId = ' . $userId;
        $sum = $this->objDCart->query($sql);
        if ($sum === false) {
            return ResultWrapper::fail($this->objDCart->error(), ErrorCode::$dberror);
        }
        $subBuyNum = isset($sum[0]['subBuyNum']) ? $sum[0]['subBuyNum'] : 0;
        return ResultWrapper::success($subBuyNum);
    }


    /**
     * Doc: (des="组团立即购买")
     * User: XMing
     * Date: 2021/1/22
     * Time: 4:18 下午
     * @param array $lists
     * @param array $data
     * @return ResultWrapper
     * @throws \Exception
     */
    public function buyNow(array $lists, array $data): ResultWrapper
    {
        switch ($data['sourceType']) {
            case self::$sourceType['comBin']:
                $buyResult = self::buyComBin($lists, $data);
                break;
        }
        if (!$buyResult->isSuccess()) {
            return ResultWrapper::fail($buyResult->getData(), $buyResult->getErrorCode());
        }
        return ResultWrapper::success($buyResult->getData());
    }

    /**
     * 普通商品立即购买
     */
    public function goodsBuyNow(array $goods, array $data)
    {
        //获取当前用户的客户id
        $objMCustomer = new MCustomer($this->onlineEnterpriseId, $this->onlineUserId);
        $result = $objMCustomer->getCustomerData(['userCenterId'=>$this->onlineUserId],'*', true);
        if(!$result->isSuccess()){
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $customerData = $result->getData();
        if (empty($customerData)) {
            return ResultWrapper::fail('未找到客户信息', ErrorCode::$paramError);
        }
        $customerId = $customerData['id'];

        //获取客户的默认收货地址
        $objMShippingAddress = new MShippingAddress($this->onlineEnterpriseId);
        $selectParams = [
            'deleteStatus'  => StatusCode::$standard,
            'defaultStatus' => StatusCode::$standard,
            'customerId'    => $customerId
        ];
        $dbResult = $objMShippingAddress->getShippingAddressInfo($selectParams);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), ErrorCode::$dberror);
        }
        $addRessData = $dbResult->getData();
        unset($dbResult);

        //获取自提点
        $objMDeliverySetting = new MDeliverySetting($this->onlineUserId, $this->onlineEnterpriseId);
        $selfExpressRuleResult = $objMDeliverySetting->getAllSelfExpressRule();
        if (!$selfExpressRuleResult->isSuccess()) {
            return ResultWrapper::fail($selfExpressRuleResult->getData(), $selfExpressRuleResult->getErrorCode());
        }
        $selfExpressRule = $selfExpressRuleResult->getData();

        //支付方式
        $objMEnterpriseBindPayment = new MEnterpriseBindPayment($this->onlineUserId, $this->onlineEnterpriseId);
        $paymentData = $objMEnterpriseBindPayment->getPaymentType();
        if (!$paymentData->isSuccess()) {
            $paymentData = [];
        } else {
            $paymentData = $paymentData->getData();
        }

        //配送方式
        $objMDeliverySetting = new MDeliverySetting($this->onlineUserId, $this->onlineEnterpriseId);
        $DeliveryData = $objMDeliverySetting->allDelivery();
        if (!$DeliveryData->isSuccess()) {
            $DeliveryData = [];
        } else {
            $DeliveryData = array_values($DeliveryData->getData()['data']);
        }

        //当前用户选择的配送方式
        if (!empty($data['deliveryId'])) {
            $this->onlineUserDefaultDeliveryType = $data['deliveryId'];
        } else {
            if (!empty($DeliveryData)) {
                foreach ($DeliveryData as $value) {
                    if ($value['defaultStatus'] == StatusCode::$standard) {
                        $this->onlineUserDefaultDeliveryType = $value['deliveryType'];
                        break;
                    }
                }
            }
        }

        //当前用户的收货地址code 省-市-区
        if (!empty($addressId)) {
            $addressResult = $objMShippingAddress->getShippingAddressInfo(['id' => $data['addressId']]);
            if (!$addressResult->isSuccess()) {
                return ResultWrapper::fail($addressResult->getData(), ErrorCode::$dberror);
            }
            $addressInfo = $addressResult->getData();
            if (!empty($addressInfo)) {
                $this->onlineUserAddressCode = $addressInfo['provinceCode'] . '-' . $addressInfo['cityCode'] . '-' . $addressInfo['districtCode'];
            }
        } else {
            if (!empty($addRessData)) {
                $this->onlineUserAddressCode = $addRessData['provinceCode'] . '-' . $addRessData['cityCode'] . '-' . $addRessData['districtCode'];
            }
        }

        //获取当前企业设置的包邮价格
        $objMBasicSetup = new MBasicSetup($this->onlineEnterpriseId);
        $freeExpressPriceResult = $objMBasicSetup->getBasicField('freeExpressPrice');
        if (!$freeExpressPriceResult->isSuccess()) {
            return ResultWrapper::fail($freeExpressPriceResult->getData(), $freeExpressPriceResult->getData());
        }
        $freeExpressPrice = $freeExpressPriceResult->getData();
        $this->freeExpressPrice = (isset($freeExpressPrice['freeExpressPrice']) && !empty($freeExpressPrice['freeExpressPrice'])) ? $freeExpressPrice['freeExpressPrice'] : 0;

        //所有结算商品id
        $goodsIds = [$goods['goodsId']];

        $cartDataResult = self::formatGoodsAndShop([$goods], $data['couponId'], $data['vipCardId']);
        if (!$cartDataResult->isSuccess()) {
            return ResultWrapper::fail($cartDataResult->getData(), $cartDataResult->getErrorCode());
        }
        $cartData = $cartDataResult->getData();
        if (empty($cartData['goodsData'])) {
            return ResultWrapper::fail('商品已失效,不能进行结算', ErrorCode::$paramError);
        }

        if (empty($addRessData)) {
            $cartData['address'] = (object)[];
        } else {
            $objMSysAreaChina = new MSysAreaChina();
            $areaName = $objMSysAreaChina->getNameByCode([
                $addRessData['provinceCode'],
                $addRessData['cityCode'],
                $addRessData['districtCode']
            ]);
            $cartData['address'] = [
                'name'         => isset($addRessData['name']) ? $addRessData['name'] : '',
                'mobile'       => isset($addRessData['mobile']) ? $addRessData['mobile'] : '',
                'address'      => isset($addRessData['address']) ? $addRessData['address'] : '',
                'id'           => isset($addRessData['id']) ? $addRessData['id'] : '',
                'provinceName' => isset($areaName[$addRessData['provinceCode']]) ? $areaName[$addRessData['provinceCode']] : '',
                'cityName'     => isset($areaName[$addRessData['cityCode']]) ? $areaName[$addRessData['cityCode']] : '',
                'districtName' => isset($areaName[$addRessData['districtCode']]) ? $areaName[$addRessData['districtCode']] : '',
            ];
        }

        //订单使用的优惠券
        $userCouponInfo = [];
        if (!empty($data['couponId'])) {
            $objMUserCoupon = new MUserCoupon($this->onlineUserId, $this->onlineEnterpriseId);
            $dbResult = $objMUserCoupon->getUserCoupon(['id' => $data['couponId']]);
            if ($dbResult->isSuccess()) $userCouponInfo = $dbResult->getData();
            $userCouponInfo = isset($userCouponInfo[0]) ? $userCouponInfo[0] : [];
        }

        // 订单金额小于银行打款设置的启用金额,不支持银行打款
        if(!empty($paymentData)){
            foreach ($paymentData as $key => $value){
                if($value['id'] == StatusCode::$payType['bankLoans'] && $cartData['payMoney'] <= $value['limit']){
                    unset($paymentData[$key]);
                }
            }
        }

        $cartData['payment'] = array_values($paymentData);
        $cartData['useCoupon'] = $userCouponInfo;
        $cartData['delivery'] = $DeliveryData;
        $cartData['selfExpressRule'] = $selfExpressRule;
        //优惠券数据
        $cartData = self::findCoupon($cartData);

        //获取订单会员卡
        $vipCard = self::getVipCard($goodsIds);
        if ($vipCard->isSuccess()) {
            $vipCard = $vipCard->getData();
        } else {
            $vipCard = [];
        }
        $cartData['vipCard'][] = $vipCard;

        // 客户余额
        $cartData['balance'] = $customerData['money'];

        // 会员余额
        $cartData['memberBalance'] = $customerData['memberBalance'];
        return ResultWrapper::success($cartData);
    }



    /**
     * Doc: (des="购买组合套餐")
     * User: XMing
     * Date: 2021/1/22
     * Time: 4:34 下午
     * @param array $lists
     * @param array $data
     * @return ResultWrapper
     * @throws \Exception
     */
    private function buyComBin(array $lists, array $data): ResultWrapper
    {
        $objMCustomer = new MCustomer($this->onlineEnterpriseId,$this->onlineUserId);
        $customerId = $objMCustomer->getCustomerIdByUserCenterId($this->onlineUserId);
        //验证组合套餐
        if (empty($data['comBinId'])) {
            return ResultWrapper::fail('组合套餐错误', ErrorCode::$paramError);
        }
        $objMComBinPackage = new MComBinPackage($this->onlineEnterpriseId, $this->onlineUserId);
        $detailsResult = $objMComBinPackage->details($data['comBinId']);
        if (!$detailsResult->isSuccess()) {
            return ResultWrapper::fail($detailsResult->getData(), $detailsResult->getErrorCode());
        }
        $details = $detailsResult->getData();
        if (empty($details)) {
            return ResultWrapper::fail('未获取到指定的组合套餐', ErrorCode::$paramError);
        }

        $time = time();
        if ($details['isExpire'] == StatusCode::$standard) {
            if ($time < $details['startTime']) {
                return ResultWrapper::fail('组合套餐活动未开始', ErrorCode::$paramError);
            }
            if ($time > $details['endTime']) {
                return ResultWrapper::fail('组合套餐活动已结束', ErrorCode::$paramError);
            }
        }

        if ($details['isLimit'] == StatusCode::$standard) {
            //获取用户组合套餐购买数量(TODO)
            $user_log_num = 0;
            if ($user_log_num >= $details['limitNum']) {
                return ResultWrapper::fail('你已经购买过此组合套餐了', ErrorCode::$paramError);
            }
        }

        //验证商品在售状态，sku是否参与组合，库存
        $allowSkuArr = [];
        $goodsData = $details['goodsData'];
        $mapping = [];//商品映射
        foreach ($goodsData as $goods) {
            $sku = $goods['joinSku'];
            $allowSkuArr = array_merge($sku, $allowSkuArr);
            $mapping[$goods['id']] = $goods;
        }

        //验证商品完整性
        $goodsIdArr = explode(',', $details['goodsIds']);
        $selectGoodsIdArr = [];
        foreach ($lists as $val) {
            if (!in_array($val['goodsId'], $selectGoodsIdArr)) {
                $selectGoodsIdArr[] = $val['goodsId'];
            }

            $thisRow = getArrayItem($mapping,$val['goodsId'],[]);
            if (isset($thisRow['joinSku']) && !empty($thisRow['joinSku'])){
                if (!in_array($val['skuId'], $allowSkuArr)) {
                    return ResultWrapper::fail('商品规格选择异常', ErrorCode::$paramError);
                }
            }
        }
        $diffBool = array_diff($goodsIdArr, $selectGoodsIdArr);
        if (!empty($diffBool)) {
            return ResultWrapper::fail('你还没选择完组合商品', ErrorCode::$paramError);
        }
        unset($diffBool);

        //店铺
        $objMGoods = new MShop($this->onlineEnterpriseId, $this->onlineUserId);
        $shopResult = $objMGoods->getShopByIdd($details['shopId']);
        if (!$shopResult->isSuccess()) {
            return ResultWrapper::fail($shopResult->getData(), $shopResult->getErrorCode());
        }
        $shop = $shopResult->getData();

        $c_goods_Lists = [];
        foreach ($lists as $val) {
            if (!isset($mapping[$val['goodsId']])) {
                return ResultWrapper::fail('商品信息异常', ErrorCode::$paramError);
            }
            $row = getArrayItem($mapping, $val['goodsId'], []);
            $specMultiple = getArrayItem($row, 'specMultiple', []);
            $specMultipleMap = [];//sku映射
            foreach ($specMultiple as $value) {
                $specMultipleMap[$value['id']] = $value;
            }

            if ($row['enableStatus'] == StatusCode::$delete) {
                return ResultWrapper::fail('组合套餐商品下架了', ErrorCode::$paramError);
            }

            $skuRow = getArrayItem($specMultipleMap, $val['skuId'], []);
            if ($this->preSale == StatusCode::$delete) {
                if ($skuRow['inventory'] < 1) {
                    return ResultWrapper::fail('组合套餐库存不足', ErrorCode::$paramError);
                }
            }

            $c_goods_Lists[] = [
                'goodsId'       => getArrayItem($row, 'id', 0),
                'goodsCode'     => createCode(StatusCode::$code['goodsBasic']['prefix'], $row['basicGoodsId'], StatusCode::$code['goodsBasic']['length']),
                'buyNum'        => 1,
                'shopId'        => getArrayItem($row, 'shopId', 0),
                'shopLogo'      => getArrayItem($shop, 'logo', ''),
                'goodsBasicId'  => getArrayItem($row, 'basicGoodsId', 0),
                'selection'     => StatusCode::$standard,
                'skuId'         => $val['skuId'],
                'warehouseId'   => getArrayItem($row, 'warehouseId', 0),
                'activityId'    => 0,
                'shopName'      => getArrayItem($row, 'shopName', ''),
                'brandName'     => getArrayItem($row, 'brandName', ''),
                'categoryName'  => getArrayItem($row, 'categoryName', ''),
                'barCode'       => getArrayItem($skuRow, 'barCode', ''),
                'storageCode'   => getArrayItem($row, 'storageCode', ''),
                'goodsName'     => getArrayItem($row, 'title', ''),
                'goodsImages'   => array_shift($row['images']),
                'isInvalid'     => StatusCode::$standard,
                'describe'      => getArrayItem($row, 'describe', ''),
                'categoryId'    => getArrayItem($row, 'categoryId', 0),
                'brandId'       => getArrayItem($row, 'brandId', 0),
                'categoryPath'  => getArrayItem($row, 'categoryPath', ''),
                'isActivity'    => StatusCode::$delete,
                'specType'      => getArrayItem($row, 'specType', 0),
                'unitName'      => getArrayItem($skuRow, 'unitName', ''),
                'specGroup'     => getArrayItem($skuRow, 'specGroup', []),
                'notExpress'    => StatusCode::$standard,
                'supplierId'    => getArrayItem($row, 'supplierId', 0),
                'express'       => [
                    "weight"      => getArrayItem($skuRow, 'weight', 0),
                    "expressType" => getArrayItem($row, 'expressType', 0),
                    "ruleId"      => getArrayItem($row, 'ruleId', 0),
                    "expressFee"  => getArrayItem($row, 'expressFee', 0),
                ],
                'originPrice'   => getArrayItem($skuRow, 'marketPrice', 0),
                'price'         => getArrayItem($skuRow, 'salePrice', 0),
                'preferential'  => 0,
                'totalMoney'    => getArrayItem($skuRow, 'salePrice', 0),
                'activityMoney' => 0,
                'isMutex'       => StatusCode::$delete,
                'costPrice'     => getArrayItem($skuRow, 'costPrice', 0),
                'inventoryNum'  => getArrayItem($skuRow, 'inventory', 0),
                'conversion'    => getArrayItem($skuRow, 'conversion', 0),
                'expressMoney'  => 0,
            ];
        }


        //获取客户的默认收货地址
        $objMShippingAddress = new MShippingAddress($this->onlineEnterpriseId);
        $selectParams = [
            'deleteStatus'  => StatusCode::$standard,
            'defaultStatus' => StatusCode::$standard,
            'customerId'    => $customerId
        ];
        $dbResult = $objMShippingAddress->getShippingAddressInfo($selectParams);
        if (!$dbResult->isSuccess()) {
            return ResultWrapper::fail($dbResult->getData(), ErrorCode::$dberror);
        }
        $addRessData = $dbResult->getData();
        unset($dbResult);

        //获取自提点
        $objMDeliverySetting = new MDeliverySetting($this->onlineUserId, $this->onlineEnterpriseId);
        $selfExpressRuleResult = $objMDeliverySetting->getAllSelfExpressRule();
        if (!$selfExpressRuleResult->isSuccess()) {
            return ResultWrapper::fail($selfExpressRuleResult->getData(), $selfExpressRuleResult->getErrorCode());
        }
        $selfExpressRule = $selfExpressRuleResult->getData();

        //支付方式
        $objMEnterpriseBindPayment = new MEnterpriseBindPayment($this->onlineUserId, $this->onlineEnterpriseId);
        $paymentData = $objMEnterpriseBindPayment->getPaymentType();
        if (!$paymentData->isSuccess()) {
            return ResultWrapper::fail($paymentData->getData(),$paymentData->getErrorCode());
        }
        $paymentData = $paymentData->getData();


        //配送方式
        $objMDeliverySetting = new MDeliverySetting($this->onlineUserId, $this->onlineEnterpriseId);
        $DeliveryData = $objMDeliverySetting->allDelivery();
        if (!$DeliveryData->isSuccess()) {
            return ResultWrapper::fail($DeliveryData->getData(),$DeliveryData->getErrorCode());
        }
        $DeliveryData = array_values($DeliveryData->getData()['data']);

        $deliveryId = getArrayItem($data,'deliveryId',0);
        //当前用户选择的配送方式
        if (!empty($deliveryId)) {
            $this->onlineUserDefaultDeliveryType = $deliveryId;
        } else {
            if (!empty($DeliveryData)) {
                foreach ($DeliveryData as $value) {
                    if ($value['defaultStatus'] == StatusCode::$standard) {
                        $this->onlineUserDefaultDeliveryType = $value['deliveryType'];
                        break;
                    }
                }
            }
        }

        //当前用户的收货地址code 省-市-区
        $addressId = getArrayItem($data,'addressId',0);
        if (!empty($addressId)) {
            $addressResult = $objMShippingAddress->getShippingAddressInfo(['id' => $addressId]);
            if (!$addressResult->isSuccess()) {
                return ResultWrapper::fail($addressResult->getData(), ErrorCode::$dberror);
            }
            $addressInfo = $addressResult->getData();
            if (!empty($addressInfo)) {
                $this->onlineUserAddressCode = $addressInfo['provinceCode'] . '-' . $addressInfo['cityCode'] . '-' . $addressInfo['districtCode'];
            }
        } else {
            if (!empty($addRessData)) {
                $this->onlineUserAddressCode = $addRessData['provinceCode'] . '-' . $addRessData['cityCode'] . '-' . $addRessData['districtCode'];
            }
        }

        if (empty($addRessData)) {
            $address = (object)[];
        } else {
            $objMSysAreaChina = new MSysAreaChina();
            $areaName = $objMSysAreaChina->getNameByCode([
                $addRessData['provinceCode'],
                $addRessData['cityCode'],
                $addRessData['districtCode']
            ]);
            $address = [
                'name'         => isset($addRessData['name']) ? $addRessData['name'] : '',
                'mobile'       => isset($addRessData['mobile']) ? $addRessData['mobile'] : '',
                'address'      => isset($addRessData['address']) ? $addRessData['address'] : '',
                'id'           => isset($addRessData['id']) ? $addRessData['id'] : '',
                'provinceName' => isset($areaName[$addRessData['provinceCode']]) ? $areaName[$addRessData['provinceCode']] : '',
                'cityName'     => isset($areaName[$addRessData['cityCode']]) ? $areaName[$addRessData['cityCode']] : '',
                'districtName' => isset($areaName[$addRessData['districtCode']]) ? $areaName[$addRessData['districtCode']] : '',
            ];
        }

        //获取当前企业设置的包邮价格
        $objMBasicSetup = new MBasicSetup($this->onlineEnterpriseId);
        $freeExpressPriceResult = $objMBasicSetup->getBasicField('freeExpressPrice');
        if (!$freeExpressPriceResult->isSuccess()) {
            return ResultWrapper::fail($freeExpressPriceResult->getData(), $freeExpressPriceResult->getData());
        }
        $freeExpressPrice = $freeExpressPriceResult->getData();
        $this->freeExpressPrice = (isset($freeExpressPrice['freeExpressPrice']) && !empty($freeExpressPrice['freeExpressPrice'])) ? $freeExpressPrice['freeExpressPrice'] : 0;

        //组合套餐是否计算运费
        if ($details['expressType'] = StatusCode::$delete){
            //计算运费
            $result = self::calExpress($c_goods_Lists);
            if (!$result->isSuccess()) {
                return ResultWrapper::fail($result->getData(), $result->getErrorCode());
            }
        }

        $c_shop = [
            'shopId'        => getArrayItem($shop, 'id', 0),
            'shopName'      => getArrayItem($shop, 'name', ''),
            'shopLogo'      => getArrayItem($shop, 'logo', ''),
            'expressMoney'  => $this->expressMoney,
            'totalMoney'    => floatval($details['price']),
            'preferential'  => 0,
            'payMoney'      => bcadd(floatval($details['price']),$this->expressMoney,2),
            'shopGoodsData' => $c_goods_Lists
        ];


        $ret = [
            'goodsData'         => [$c_shop],
            'totalMoney'        => floatval($details['price']),
            'payMoney'          => bcadd(floatval($details['price']),$this->expressMoney,2),
            'preferential'      => 0,
            'vipDiscount'       => 0,
            'vipDoubleDiscount' => 0,
            'activityMoney'     => 0,
            'expressMoney'      => $this->expressMoney,
            'checkNum'          => 0,
            'cartNum'           => 0,
            'goodsNum'          => 0,
            'invalidData'       => [],
            'address'           => $address,
            'useCoupon'         => [],
            'payment'           => $paymentData,
            'delivery'          => $DeliveryData,
            'selfExpressRule'   => $selfExpressRule,
            'coupon'            => [],
            'vipCard'           => [],
            'comBinId'          => $data['comBinId']
        ];
        return ResultWrapper::success($ret);
    }
}