<?php


namespace App\Services\tcc;


use Api\PhpUtils\Common\BaseConvert;
use App\Exception\custom\GoodsException;
use App\Models\goods\mysql\GoodsSku;
use App\Models\goods\mysql\Tcc;

class TccService
{
    const TCC_RESULT_FAIL = false;
    const TCC_RESULT_SUCCESS = true;

    const TYPE_CANCEL_ORDER = 1;
    const TYPE_BACK_ORDER = 2;

    /**
     * 下单 商品tcc try
     * @param $keys
     * @param $tid
     * @return int
     */
    public static function placeAnOrderTry($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $goodsSkuIds = $numList = [];
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                return self::TCC_RESULT_FAIL;
            }
            $goodsSkuIds[] = $item["goods_sku_id"];
            $numList[$item["goods_sku_id"]] = $item["num"];
        }

        //有结果返回原来的结果，幂等
        $tccInfoList = Tcc::getRecordMaster(["tid" => $tid, "goods_sku_id" => $goodsSkuIds, "type" => Tcc::STATUS_TRY]);
        if (!empty($tccInfoList)) {
            foreach ($tccInfoList as $tccInfo) {
                if ($tccInfo["operator_result"] == Tcc::OPERATOR_RESULT_FAIL) {
                    return self::TCC_RESULT_FAIL;
                }
            }
            return self::TCC_RESULT_SUCCESS;
        }

        //如果已经有 confirm 或者 cancel，则直接失败
        $cTccInfoList = Tcc::getRecordMaster(["tid" => $tid, "goods_sku_id" => $goodsSkuIds, "type" => [Tcc::STATUS_CONFIRM, Tcc::STATUS_CANCEL], "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS]);
        if (!empty($cTccInfoList)) {
            foreach ($goodsSkuIds as $goodsSkuId) {
                Tcc::save([
                    "tid" => $tid,
                    "goods_sku_id" => $goodsSkuId,
                    "type" => Tcc::STATUS_TRY,
                    "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                ]);
            }
            return self::TCC_RESULT_FAIL;
        }

        $skus = GoodsSku::getRecords(["goods_sku_id" => $goodsSkuIds]
            , ["goods_sku_id", "inventory_rest", "inventory_lock", "online_status"]);

        if (!empty($skus)) {
            GoodsSku::beginTransaction();
            foreach ($skus as $sku) {
                $goodsSkuId = $sku["goods_sku_id"];

                if (!empty($sku["inventory_rest"]) && $sku["online_status"] == GoodsSku::ONLINE_STATUS_ONLINE) {
                    if (empty($numList[$goodsSkuId])) {
                        return self::TCC_RESULT_FAIL;
                    }
                    $num = $numList[$goodsSkuId];

                    //条件加上乐观锁
                    $rowCount = GoodsSku::save([
                        "inventory_lock" => $sku["inventory_lock"] + $num,
                        "inventory_rest" => $sku["inventory_rest"] - $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_rest[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        Tcc::save([
                            "tid" => $tid,
                            "goods_sku_id" => $goodsSkuId,
                            "type" => Tcc::STATUS_TRY,
                            "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                        ]);
                        throw new GoodsException(["cus" => 24]);
                    }
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_TRY,
                        "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                    ]);
                } else {
                    GoodsSku::rollback();

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_TRY,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                    return self::TCC_RESULT_FAIL;
                }
            }

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();

                foreach ($skus as $sku) {
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $sku["goods_sku_id"],
                        "type" => Tcc::STATUS_TRY,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                }
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 下单 商品tcc confirm
     * @param $keys
     * @param $tid
     * @return int
     */
    public static function placeAnOrderConfirm($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $goodsSkuIds = $numList = [];
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                return self::TCC_RESULT_FAIL;
            }
            $goodsSkuIds[] = $item["goods_sku_id"];
            $numList[$item["goods_sku_id"]] = $item["num"];
        }

        //有结果返回原来的结果，幂等
        $tccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CONFIRM
        ]);
        if (!empty($tccInfoList)) {
            foreach ($tccInfoList as $tccInfo) {
                if ($tccInfo["operator_result"] == Tcc::OPERATOR_RESULT_FAIL) {
                    return self::TCC_RESULT_FAIL;
                }
            }
            return self::TCC_RESULT_SUCCESS;
        }

        //在confirm时候必须已经有try，如果没有就直接返回fail
        $tTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_TRY,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);

        if (empty($tTccInfoList) || count($tTccInfoList) != count($goodsSkuIds)) {
            foreach ($goodsSkuIds as $goodsSkuId) {
                Tcc::save([
                    "tid" => $tid,
                    "goods_sku_id" => $goodsSkuId,
                    "type" => Tcc::STATUS_CONFIRM,
                    "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                ]);
            }
            return self::TCC_RESULT_FAIL;
        }
        //如果已经 cancel 成功, 则失败
        $cTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CANCEL,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);
        if (!empty($cTccInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $skus = GoodsSku::getRecords(["goods_sku_id" => $goodsSkuIds]
            , ["goods_sku_id", "total_amount_order", "inventory_lock", "online_status"]);
        if (!empty($skus)) {
            GoodsSku::beginTransaction();
            foreach ($skus as $sku) {
                $goodsSkuId = $sku["goods_sku_id"];

                if (!empty($sku["inventory_lock"]) && $sku["online_status"] == GoodsSku::ONLINE_STATUS_ONLINE) {
                    if (empty($numList[$goodsSkuId])) {
                        return self::TCC_RESULT_FAIL;
                    }
                    $num = $numList[$goodsSkuId];

                    //条件加上乐观锁
                    $rowCount = GoodsSku::save([
                        "inventory_lock" => $sku["inventory_lock"] - $num,
                        "total_amount_order" => $sku["total_amount_order"] + $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        Tcc::save([
                            "tid" => $tid,
                            "goods_sku_id" => $goodsSkuId,
                            "type" => Tcc::STATUS_CONFIRM,
                            "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                        ]);
                        throw new GoodsException(["cus" => 31]);
                    }

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CONFIRM,
                        "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                    ]);
                } else {
                    GoodsSku::rollback();

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CONFIRM,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                    return self::TCC_RESULT_FAIL;
                }
            }

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();

                foreach ($skus as $sku) {
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $sku["goods_sku_id"],
                        "type" => Tcc::STATUS_CONFIRM,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                }
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 下单 商品tcc cancel
     * @param $keys
     * @param $tid
     * @return int
     */
    public static function placeAnOrderCancel($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $goodsSkuIds = $numList = [];
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                return self::TCC_RESULT_FAIL;
            }
            $goodsSkuIds[] = $item["goods_sku_id"];
            $numList[$item["goods_sku_id"]] = $item["num"];
        }

        //有结果返回原来的结果，幂等
        $tccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CANCEL
        ]);
        if (!empty($tccInfoList)) {
            foreach ($tccInfoList as $tccInfo) {
                if ($tccInfo["operator_result"] == Tcc::OPERATOR_RESULT_FAIL) {
                    return self::TCC_RESULT_FAIL;
                }
            }
            return self::TCC_RESULT_SUCCESS;
        }

        //允许空cancel(没有try情况)
        //在cancel时候必须已经有try，如果没有就直接返回success
        $tTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_TRY,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);
        if (empty($tTccInfoList) || count($tTccInfoList) != count($goodsSkuIds)) {
            foreach ($goodsSkuIds as $goodsSkuId) {
                Tcc::save([
                    "tid" => $tid,
                    "goods_sku_id" => $goodsSkuId,
                    "type" => Tcc::STATUS_CANCEL,
                    "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                ]);
            }
            return self::TCC_RESULT_SUCCESS;
        }
        //如果已经 confirm 成功, 则失败
        $cTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CONFIRM,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);
        if (!empty($cTccInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $skus = GoodsSku::getRecords(["goods_sku_id" => $goodsSkuIds]
            , ["goods_sku_id", "inventory_rest", "inventory_lock", "online_status"]);
        if (!empty($skus)) {
            GoodsSku::beginTransaction();
            foreach ($skus as $sku) {
                $goodsSkuId = $sku["goods_sku_id"];

                if (!empty($sku["inventory_lock"]) && $sku["online_status"] == GoodsSku::ONLINE_STATUS_ONLINE) {
                    if (empty($numList[$goodsSkuId])) {
                        return self::TCC_RESULT_FAIL;
                    }
                    $num = $numList[$goodsSkuId];

                    //条件加上乐观锁
                    $rowCount = GoodsSku::save([
                        "inventory_lock" => $sku["inventory_lock"] - $num,
                        "inventory_rest" => $sku["inventory_rest"] + $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        Tcc::save([
                            "tid" => $tid,
                            "goods_sku_id" => $goodsSkuId,
                            "type" => Tcc::STATUS_CANCEL,
                            "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                        ]);
                        throw new GoodsException(["cus" => 31]);
                    }
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CANCEL,
                        "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                    ]);
                } else {
                    GoodsSku::rollback();

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CANCEL,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                    return self::TCC_RESULT_FAIL;
                }
            }

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();

                foreach ($skus as $sku) {
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $sku["goods_sku_id"],
                        "type" => Tcc::STATUS_CANCEL,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                }
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 取消订单单 商品tcc try
     * @param $keys
     * @param $tid
     * @return int
     */
    public static function cancelOrderTry($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $goodsSkuIds = $numList = [];
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                return self::TCC_RESULT_FAIL;
            }
            $goodsSkuIds[] = $item["goods_sku_id"];
            $numList[$item["goods_sku_id"]] = $item["num"];
        }

        //有结果返回原来的结果，幂等
        $tccInfoList = Tcc::getRecordMaster(["tid" => $tid, "goods_sku_id" => $goodsSkuIds, "type" => Tcc::STATUS_TRY]);
        if (!empty($tccInfoList)) {
            foreach ($tccInfoList as $tccInfo) {
                if ($tccInfo["operator_result"] == Tcc::OPERATOR_RESULT_FAIL) {
                    return self::TCC_RESULT_FAIL;
                }
            }
            return self::TCC_RESULT_SUCCESS;
        }

        //如果已经有 confirm 或者 cancel，则直接失败
        $cTccInfoList = Tcc::getRecordMaster(["tid" => $tid, "goods_sku_id" => $goodsSkuIds, "type" => [Tcc::STATUS_CONFIRM, Tcc::STATUS_CANCEL], "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS]);
        if (!empty($cTccInfoList)) {
            foreach ($goodsSkuIds as $goodsSkuId) {
                Tcc::save([
                    "tid" => $tid,
                    "goods_sku_id" => $goodsSkuId,
                    "type" => Tcc::STATUS_TRY,
                    "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                ]);
            }
            return self::TCC_RESULT_FAIL;
        }

        $skus = GoodsSku::getRecords(["goods_sku_id" => $goodsSkuIds]
            , ["goods_sku_id", "total_amount_order", "inventory_lock", "online_status"]);

        if (!empty($skus)) {
            GoodsSku::beginTransaction();
            foreach ($skus as $sku) {
                $goodsSkuId = $sku["goods_sku_id"];

                if (!empty($sku["total_amount_order"])) {
                    if (empty($numList[$goodsSkuId])) {
                        return self::TCC_RESULT_FAIL;
                    }
                    $num = $numList[$goodsSkuId];

                    //条件加上乐观锁
                    $rowCount = GoodsSku::save([
                        "inventory_lock" => $sku["inventory_lock"] + $num,
                        "total_amount_order" => $sku["total_amount_order"] - $num,
                    ], ["goods_sku_id" => $goodsSkuId, "total_amount_order[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        Tcc::save([
                            "tid" => $tid,
                            "goods_sku_id" => $goodsSkuId,
                            "type" => Tcc::STATUS_TRY,
                            "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                        ]);
                        throw new GoodsException(["cus" => 32]);
                    }

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_TRY,
                        "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                    ]);
                } else {
                    GoodsSku::rollback();

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_TRY,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                    return self::TCC_RESULT_FAIL;
                }
            }

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();

                foreach ($skus as $sku) {
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $sku["goods_sku_id"],
                        "type" => Tcc::STATUS_TRY,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                }
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 取消订单单 商品tcc confirm
     * @param $keys
     * @param $tid
     * @param $type
     * @return int
     */
    public static function cancelOrderConfirm($keys, $tid, $type)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $goodsSkuIds = $numList = [];
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                return self::TCC_RESULT_FAIL;
            }
            $goodsSkuIds[] = $item["goods_sku_id"];
            $numList[$item["goods_sku_id"]] = $item["num"];
        }

        //有结果返回原来的结果，幂等
        $tccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CONFIRM
        ]);
        if (!empty($tccInfoList)) {
            foreach ($tccInfoList as $tccInfo) {
                if ($tccInfo["operator_result"] == Tcc::OPERATOR_RESULT_FAIL) {
                    return self::TCC_RESULT_FAIL;
                }
            }
            return self::TCC_RESULT_SUCCESS;
        }

        //在confirm时候必须已经有try，如果没有就直接返回fail
        $cTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_TRY,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);

        if (empty($cTccInfoList) || count($cTccInfoList) != count($goodsSkuIds)) {
            foreach ($goodsSkuIds as $goodsSkuId) {
                Tcc::save([
                    "tid" => $tid,
                    "goods_sku_id" => $goodsSkuId,
                    "type" => Tcc::STATUS_CONFIRM,
                    "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                ]);
            }
            return self::TCC_RESULT_FAIL;
        }
        //如果已经 cancel 成功, 则失败
        $cTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CANCEL,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);
        if (!empty($cTccInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $skus = GoodsSku::getRecords(["goods_sku_id" => $goodsSkuIds]
            , ["goods_sku_id", "total_amount_sold", "inventory_rest", "inventory_lock", "online_status"]);
        if (!empty($skus)) {
            GoodsSku::beginTransaction();
            foreach ($skus as $sku) {
                $goodsSkuId = $sku["goods_sku_id"];

                if (!empty($sku["inventory_lock"])) {
                    if (empty($numList[$goodsSkuId])) {
                        return self::TCC_RESULT_FAIL;
                    }
                    $num = $numList[$goodsSkuId];

                    if ($type == self::TYPE_CANCEL_ORDER) {
                        $params = [
                            "inventory_lock" => $sku["inventory_lock"] - $num,
                            "inventory_rest" => $sku["inventory_rest"] + $num,
                        ];
                        $rowCount = GoodsSku::save($params, ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                        if ($rowCount <= 0) {
                            GoodsSku::rollback();
                            Tcc::save([
                                "tid" => $tid,
                                "goods_sku_id" => $goodsSkuId,
                                "type" => Tcc::STATUS_CONFIRM,
                                "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                            ]);
                            throw new GoodsException(["cus" => 31]);
                        }
                    }
                    if ($type == self::TYPE_BACK_ORDER) {
                        $params = [
                            "inventory_lock" => $sku["inventory_lock"] - $num,
                            "inventory_rest" => $sku["inventory_rest"] + $num,
                            "total_amount_sold" => $sku["total_amount_sold"] - $num,
                        ];
                        $rowCount = GoodsSku::save($params,
                            ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num, "total_amount_sold[>=]" => $num]);
                        if ($rowCount <= 0) {
                            GoodsSku::rollback();
                            Tcc::save([
                                "tid" => $tid,
                                "goods_sku_id" => $goodsSkuId,
                                "type" => Tcc::STATUS_CONFIRM,
                                "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                            ]);
                            throw new GoodsException(["cus" => 32]);
                        }
                    }

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CONFIRM,
                        "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                    ]);
                } else {
                    GoodsSku::rollback();

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CONFIRM,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                    return self::TCC_RESULT_FAIL;
                }
            }

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();

                foreach ($skus as $sku) {
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $sku["goods_sku_id"],
                        "type" => Tcc::STATUS_CONFIRM,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                }
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 取消订单单 商品tcc cancel
     * @param $keys
     * @param $tid
     * @return int
     */
    public static function cancelOrderCancel($keys, $tid)
    {
        $goodsInfoList = json_decode($keys, true);
        if (empty($goodsInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $goodsSkuIds = $numList = [];
        foreach ($goodsInfoList as $item) {
            if (empty($item["goods_sku_id"]) || empty($item["num"])) {
                return self::TCC_RESULT_FAIL;
            }
            $goodsSkuIds[] = $item["goods_sku_id"];
            $numList[$item["goods_sku_id"]] = $item["num"];
        }

        //有结果返回原来的结果，幂等
        $tccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CANCEL
        ]);
        if (!empty($tccInfoList)) {
            foreach ($tccInfoList as $tccInfo) {
                if ($tccInfo["operator_result"] == Tcc::OPERATOR_RESULT_FAIL) {
                    return self::TCC_RESULT_FAIL;
                }
            }
            return self::TCC_RESULT_SUCCESS;
        }

        //允许空cancel(没有try情况)
        //在cancel时候必须已经有try，如果没有就直接返回success
        $cTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_TRY,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);
        if (empty($cTccInfoList) || count($cTccInfoList) != count($goodsSkuIds)) {
            foreach ($goodsSkuIds as $goodsSkuId) {
                Tcc::save([
                    "tid" => $tid,
                    "goods_sku_id" => $goodsSkuId,
                    "type" => Tcc::STATUS_CANCEL,
                    "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                ]);
            }
            return self::TCC_RESULT_SUCCESS;
        }

        //如果已经 confirm 成功, 则失败
        $cTccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => Tcc::STATUS_CONFIRM,
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS
        ]);
        if (!empty($cTccInfoList)) {
            return self::TCC_RESULT_FAIL;
        }

        $skus = GoodsSku::getRecords(["goods_sku_id" => $goodsSkuIds]
            , ["goods_sku_id", "total_amount_order", "inventory_lock", "online_status"]);
        if (!empty($skus)) {
            GoodsSku::beginTransaction();
            foreach ($skus as $sku) {
                $goodsSkuId = $sku["goods_sku_id"];

                if (!empty($sku["inventory_lock"])) {
                    if (empty($numList[$goodsSkuId])) {
                        return self::TCC_RESULT_FAIL;
                    }
                    $num = $numList[$goodsSkuId];

                    $rowCount = GoodsSku::save([
                        "inventory_lock" => $sku["inventory_lock"] - $num,
                        "total_amount_order" => $sku["total_amount_order"] + $num,
                    ], ["goods_sku_id" => $goodsSkuId, "inventory_lock[>=]" => $num]);
                    if ($rowCount <= 0) {
                        GoodsSku::rollback();
                        Tcc::save([
                            "tid" => $tid,
                            "goods_sku_id" => $goodsSkuId,
                            "type" => Tcc::STATUS_CANCEL,
                            "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                        ]);
                        throw new GoodsException(["cus" => 31]);
                    }

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CANCEL,
                        "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
                    ]);
                } else {
                    GoodsSku::rollback();

                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $goodsSkuId,
                        "type" => Tcc::STATUS_CANCEL,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                    return self::TCC_RESULT_FAIL;
                }
            }

            if (!GoodsSku::commit()) {
                GoodsSku::rollback();

                foreach ($skus as $sku) {
                    Tcc::save([
                        "tid" => $tid,
                        "goods_sku_id" => $sku["goods_sku_id"],
                        "type" => Tcc::STATUS_CANCEL,
                        "operator_result" => Tcc::OPERATOR_RESULT_FAIL,
                    ]);
                }
            }
        }

        return self::TCC_RESULT_SUCCESS;
    }

    /**
     * 判断一个tcc是否完成
     * @param $goodsSkuIdStr
     * @param $tid
     * @return int
     */
    public static function tccIsFinish($goodsSkuIdStr, $tid)
    {
        $goodsSkuIds = explode(",", $goodsSkuIdStr);
        $tccInfoList = Tcc::getRecordMaster([
            "tid" => $tid,
            "goods_sku_id" => $goodsSkuIds,
            "type" => [Tcc::STATUS_CONFIRM, Tcc::STATUS_CANCEL],
            "operator_result" => Tcc::OPERATOR_RESULT_SUCCESS,
        ]);

        if (!empty($tccInfoList) && count($tccInfoList) == count($goodsSkuIds)) {
            return self::TCC_RESULT_SUCCESS;
        }
        return self::TCC_RESULT_FAIL;
    }
}