| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945 |
- <?php
- namespace bizHd\product\classes;
- use biz\item\classes\PtItemClass;
- use bizHd\base\classes\BaseClass;
- use bizHd\custom\classes\CustomClass;
- use common\components\imgUtil;
- use common\components\noticeUtil;
- use common\components\sms;
- use common\components\util;
- use PhpAmqpLib\Wire\AMQPTable;
- use Yii;
- class ProductClass extends BaseClass
- {
- const LIMIT_BUY_KEY = 'hd_limit_buy:'; // 零售客户与批发客户不同,这个要与批发不同
- const CLEAR_MARK_KEY = 'limit_buy_clear_at:';
- const CLEAR_LOOP_KEY = 'limit_buy_clear_loop:';
- const LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY = 'limitBuyRollbackSnapshot'; //记录限购旧值快照缓存键
- public static $baseFile = '\bizHd\product\models\Product';
- //花材列表用到的数据组合 ssh 20221225
- public static function itemListGroup($list, $level = 0)
- {
- //各个等级对应的price addPrice字段
- $priceMap = \bizGhs\custom\classes\CustomClass::$levelPriceKeyMap;
- $addPriceMap = \bizGhs\custom\classes\CustomClass::$levelAddPriceKeyMap;
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $cover = imgUtil::getPrefix() . 'hhb_small.png';
- if (!empty($shortCover)) {
- $parse = parse_url(self::groupImg($shortCover));
- if (empty($parse['query'])) {
- $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- } else {
- $cover = self::groupImg($shortCover);
- }
- }
- $list[$k]['cover'] = $cover;
- //今日特价、会员价
- $price = \bizGhs\product\classes\ProductClass::getFinalPrice($v, $level, $priceMap, $addPriceMap);
- if ($level == 0) {
- //零售做特价时显示的原价,不能删除
- if (isset($v['skPrice'])) {
- $list[$k]['prePrice'] = $v['skPrice'];
- }
- }
- if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
- //做特价花材时,花材列表和改价列表显示的会员价和零售价,不能删除
- $list[$k]['hjPrice'] = floatval($v['hjDiscountPrice']);
- $list[$k]['skPrice'] = floatval($v['skDiscountPrice']);
- }
- $list[$k]['price'] = $price;
- $cost = $v['cost'] ?? 0;
- if ($price > 0) {
- $profit = bcsub($price, $cost, 2);
- $mll = bcdiv($profit, $price, 3);
- $mll = bcmul($mll, 100);
- $mll = round($mll, 1);
- } else {
- $mll = 0;
- }
- $list[$k]['mll'] = $mll;
- }
- return $list;
- }
- public static function kdItemGroup($list, $level = 0)
- {
- //各个等级对应的price addPrice字段
- $priceMap = \bizGhs\custom\classes\CustomClass::$levelPriceKeyMap;
- $addPriceMap = \bizGhs\custom\classes\CustomClass::$levelAddPriceKeyMap;
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $cover = imgUtil::getPrefix() . 'hhb_small.png';
- if (!empty($shortCover)) {
- $parse = parse_url(self::groupImg($shortCover));
- if (empty($parse['query'])) {
- $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- } else {
- $cover = self::groupImg($shortCover);
- }
- }
- $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
- $list[$k]['cover'] = $cover;
- $stockInfo = self::formatStock($v['stock'], $ratio);
- $list[$k]['bigNum'] = $stockInfo['bigNum'];
- $list[$k]['smallNum'] = $stockInfo['smallNum'];
- $price = \bizGhs\product\classes\ProductClass::getFinalPrice($v, $level, $priceMap, $addPriceMap);
- $list[$k]['price'] = $price;
- $list[$k]['bigPrice'] = $price;
- $smallRatio = $v['smallRatio'] ?? 0;
- $smallPrice = bcdiv($price, $ratio, 2);
- $smallPrice = bcmul($smallPrice, $smallRatio, 2);
- $list[$k]['smallPrice'] = $smallPrice;
- }
- return $list;
- }
- //花店的花材转成批发店花材 ssh 20230308
- public static function hdToggleGhs($list, $ghsShop)
- {
- $ghsMainId = $ghsShop->mainId ?? 0;
- $ptItemIds = array_column($list, 'itemId');
- $ghsHasProduct = ProductClass::getAllByCondition(['mainId' => $ghsMainId, 'itemId' => ['in', $ptItemIds]], null, '*', 'itemId');
- $ghsSjId = $ghsShop->sjId ?? 0;
- $ghsShopId = $ghsShop->id ?? 0;
- $defaultClassId = \bizGhs\item\classes\ItemClassClass::getDefaultClassId($ghsMainId);
- if (empty($defaultClassId)) {
- util::fail('没有找到花材的默认分类');
- }
- foreach ($list as $k => $v) {
- $ptItemId = $v['itemId'] ?? 0;
- $hdProductId = $v['productId'] ?? 0;
- if (isset($ghsHasProduct[$ptItemId]) == false) {
- //不存在,则从平台新增
- $ptItem = PtItemClass::getById($ptItemId);
- unset($ptItem['addTime']);
- unset($ptItem['updateTime']);
- $productData = $ptItem;
- $productData['sjId'] = $ghsSjId;
- $productData['shopId'] = $ghsShopId;
- $productData['mainId'] = $ghsMainId;
- $productData['itemId'] = $ptItemId;
- $productData['classId'] = $defaultClassId;
- unset($productData['id']);
- $productData['rank'] = '';
- $cost = $v['cost'] ?? 0;
- $addPrice = $v['addPrice'] ?? 0;
- $productData['price'] = bcadd($cost, $addPrice, 2);
- $addProduct = ProductClass::addBaseData($productData);
- $ghsProductId = $addProduct->id ?? 0;
- } else {
- $ghsProductId = $ghsHasProduct[$ptItemId]['id'] ?? 0;
- }
- $list[$k]['productId'] = $ghsProductId;
- $list[$k]['hdProductId'] = $hdProductId;
- }
- return $list;
- }
- public static function addBaseData($data)
- {
- $respond = self::add($data, true);
- return $respond;
- }
- public static function adStockCheck($shop, $productList)
- {
- //阿东开5支老是会出现5扎情况跟踪
- $mainId = $shop->mainId ?? 0;
- Yii::info("mainId:" . $mainId);
- if (getenv('YII_ENV') == 'production') {
- if ($mainId != 742 && $mainId != 50) {
- return false;
- }
- } else {
- if ($mainId != 644) {
- return false;
- }
- }
- Yii::info('productList:' . json_encode($productList));
- if (!empty($productList)) {
- $adIds = [];
- foreach ($productList as $adItem) {
- if (isset($adItem['property']) && $adItem['property'] == 1 && $adItem['unitType'] == 0 && $adItem['num'] == 5) {
- $adIds[] = $adItem['productId'];
- Yii::info('productId:' . $adItem['productId']);
- }
- }
- if (!empty($adIds)) {
- $arr = self::getByIds($adIds);
- if (!empty($arr)) {
- foreach ($arr as $it) {
- Yii::info("num:" . $it['scanNum']);
- if (isset($it['scanNum']) && $it['scanNum'] > 0) {
- $name = $it['name'] ?? '';
- if (getenv('YII_ENV') == 'production') {
- if ($mainId == 742) {
- sms::freeSend('13531700113,' . $name, '刚刚,花材:{$var},5支被开成了5扎,请注意!!!');
- sms::freeSend('15220670540,' . $name, '刚刚,花材:{$var},5支被开成了5扎,请注意!!!');
- sms::freeSend('13531704009,' . $name, '刚刚,花材:{$var},5支被开成了5扎,请注意!!!');
- sms::freeSend('15766689130,' . $name, '刚刚,花材:{$var},5支被开成了5扎,请注意!!!');
- sms::freeSend('15280215347,' . $name, '刚刚,花材:{$var},5支被开成了5扎,请注意!!!');
- }
- if ($mainId == 50) {
- sms::freeSend('15280215347,' . $name, '刚刚,花材:{$var},5支被开成了5扎,请注意!!!');
- }
- } else {
- noticeUtil::push('刚刚,花材:' . $name . ',5支被开成了5扎,请注意!!!', '15280215347');
- }
- }
- }
- }
- }
- }
- }
- //花材的库存转换: 库存是小数,根据各个花材的unitNum, 计算出多少扎,多少支
- public static function formatStock($stock, $unitNum)
- {
- $pn = true; //正数
- if ($stock < 0) {
- $pn = false;
- $stock = abs($stock);
- }
- $bigNum = floor($stock); // 扎
- $stockArr = explode('.', $stock);
- $smallNum = 0; //支
- if (count($stockArr) === 2) {
- $smallNum = bcmul(($stock - $bigNum), $unitNum);
- }
- if (!$pn) {
- $bigNum *= -1;
- $smallNum *= -1;
- }
- return [
- 'bigNum' => $bigNum,
- 'smallNum' => $smallNum,
- ];
- }
- //通用花材列表数组组合
- public static function changePriceGroup($list, $level = 0)
- {
- //各个等级对应的price addPrice字段
- $priceMap = \bizGhs\custom\classes\CustomClass::$levelPriceKeyMap;
- $addPriceMap = \bizGhs\custom\classes\CustomClass::$levelAddPriceKeyMap;
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $cover = imgUtil::getPrefix() . 'hhb_small.png';
- if (!empty($shortCover)) {
- $parse = parse_url(self::groupImg($shortCover));
- if (empty($parse['query'])) {
- $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- } else {
- $cover = self::groupImg($shortCover);
- }
- }
- $list[$k]['cover'] = $cover;
- $list[$k]['prePrice'] = $v['price'] ?? 0;
- //今日特价、会员价
- $price = \bizGhs\product\classes\ProductClass::getFinalPrice($v, $level, $priceMap, $addPriceMap);
- if ($level == 0) {
- //零售做特价时显示的原价,不能删除
- if (isset($v['skPrice'])) {
- $list[$k]['prePrice'] = $v['skPrice'];
- }
- }
- if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
- //做特价花材时,花材列表和改价列表显示的会员价和零售价,不能删除
- $list[$k]['hjPrice'] = floatval($v['hjDiscountPrice']);
- $list[$k]['skPrice'] = floatval($v['skDiscountPrice']);
- }
- $list[$k]['price'] = $price;
- $cost = $v['avCost'] ?? 0;
- $ml = 0;
- if ($price > 0) {
- $profit = bcsub($price, $cost, 2);
- $ml = $profit;
- $mll = bcdiv($profit, $price, 3);
- $mll = bcmul($mll, 100);
- $mll = round($mll, 1);
- } else {
- $mll = 0;
- }
- $list[$k]['mll'] = $mll;
- $list[$k]['ml'] = $ml;
- }
- return $list;
- }
- public static function breakItemGroup($list, $level = 0)
- {
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 20;
- $stockInfo = self::formatStock($v['stock'], $ratio);
- $list[$k]['bigNum'] = $stockInfo['bigNum'];
- $list[$k]['smallNum'] = $stockInfo['smallNum'];
- $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- }
- return $list;
- }
- public static function partItemGroup($list, $level = 0)
- {
- $priceMap = \bizGhs\custom\classes\CustomClass::$levelPriceKeyMap;
- $addPriceMap = \bizGhs\custom\classes\CustomClass::$levelAddPriceKeyMap;
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 20;
- $stockInfo = self::formatStock($v['stock'], $ratio);
- $list[$k]['bigNum'] = $stockInfo['bigNum'];
- $list[$k]['smallNum'] = $stockInfo['smallNum'];
- $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- $price = \bizGhs\product\classes\ProductClass::getFinalPrice($v, $level, $priceMap, $addPriceMap);
- $list[$k]['price'] = $price;
- $list[$k]['bigPrice'] = $price;
- $smallRatio = $v['smallRatio'] ?? 0;
- $smallPrice = bcdiv($price, $ratio, 2);
- $smallPrice = bcmul($smallPrice, $smallRatio, 2);
- $list[$k]['smallPrice'] = $smallPrice;
- }
- return $list;
- }
- public static function checkItemGroup($list, $level = 0)
- {
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
- $stockInfo = self::formatStock($v['stock'], $ratio);
- $list[$k]['bigNum'] = $stockInfo['bigNum'];
- $list[$k]['smallNum'] = $stockInfo['smallNum'];
- }
- return $list;
- }
- public static function cgItemGroup($list, $level = 0)
- {
- $priceMap = \bizGhs\custom\classes\CustomClass::$levelPriceKeyMap;
- $addPriceMap = \bizGhs\custom\classes\CustomClass::$levelAddPriceKeyMap;
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
- $stockInfo = self::formatStock($v['stock'], $ratio);
- $list[$k]['bigNum'] = $stockInfo['bigNum'];
- $list[$k]['smallNum'] = $stockInfo['smallNum'];
- $price = \bizGhs\product\classes\ProductClass::getFinalPrice($v, $level, $priceMap, $addPriceMap);
- $list[$k]['price'] = $price;
- $list[$k]['bigPrice'] = $price;
- $smallRatio = $v['smallRatio'] ?? 0;
- $smallPrice = bcdiv($price, $ratio, 2);
- $smallPrice = bcmul($smallPrice, $smallRatio, 2);
- $list[$k]['smallPrice'] = $smallPrice;
- }
- return $list;
- }
- //通用花材列表数组组合
- public static function changeStockGroup($list, $level = 0)
- {
- //各个等级对应的price addPrice字段
- $priceMap = \bizGhs\custom\classes\CustomClass::$levelPriceKeyMap;
- $addPriceMap = \bizGhs\custom\classes\CustomClass::$levelAddPriceKeyMap;
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $cover = imgUtil::getPrefix() . 'hhb_small.png';
- if (!empty($shortCover)) {
- $parse = parse_url(self::groupImg($shortCover));
- if (empty($parse['query'])) {
- $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- } else {
- $cover = self::groupImg($shortCover);
- }
- }
- $list[$k]['cover'] = $cover;
- $list[$k]['prePrice'] = $v['price'] ?? 0;
- //今日特价、会员价
- $price = \bizGhs\product\classes\ProductClass::getFinalPrice($v, $level, $priceMap, $addPriceMap);
- //零售做特价时的原价
- if ($level == 0) {
- $currentAddPrice = $v['skMore'] ?? 0;
- $list[$k]['prePrice'] = bcadd($v['price'], $currentAddPrice, 2);
- }
- if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
- $currentAddPrice = $v['skMore'] ?? 0;
- $list[$k]['skPrice'] = bcadd($v['discountPrice'], $currentAddPrice, 2);
- }
- $list[$k]['price'] = $price;
- $cost = $v['cost'] ?? 0;
- $ml = 0;
- if ($price > 0) {
- $profit = bcsub($price, $cost, 2);
- $ml = $profit;
- $mll = bcdiv($profit, $price, 3);
- $mll = bcmul($mll, 100);
- $mll = round($mll, 1);
- } else {
- $mll = 0;
- }
- $list[$k]['mll'] = $mll;
- $list[$k]['ml'] = $ml;
- }
- return $list;
- }
- public static function stockOutGroup($list, $level = 0)
- {
- //各个等级对应的price addPrice字段
- $priceMap = \bizGhs\custom\classes\CustomClass::$levelPriceKeyMap;
- $addPriceMap = \bizGhs\custom\classes\CustomClass::$levelAddPriceKeyMap;
- foreach ($list as $k => $v) {
- $shortCover = $v['cover'] ?? '';
- $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
- $price = \bizGhs\product\classes\ProductClass::getFinalPrice($v, $level, $priceMap, $addPriceMap);
- $list[$k]['price'] = $price;
- $list[$k]['bigPrice'] = $price;
- $smallRatio = $v['smallRatio'] ?? 0;
- $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 20;
- $smallPrice = bcdiv($price, $ratio, 2);
- $smallPrice = bcmul($smallPrice, $smallRatio, 2);
- $list[$k]['smallPrice'] = $smallPrice;
- }
- return $list;
- }
- /**
- * 限购处理
- * @param $product
- * @param $customId 零售xhCustom表的id
- * @param $num
- */
- public static function handleLimitBuy($product, $customId, $num, $doCache = true)
- {
- $limitBuy = $product['limitBuy'];
- if ($limitBuy <= 0) {
- return;
- }
- $productId = $product['productId'];
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- $has = Yii::$app->redis->executeCommand('HEXISTS', [$limitKey, $customId]);
- if (!empty($has)) {
- $hasNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
- self::recordLimitBuyRollbackSnapshot($productId, $customId, true, $hasNum);
- $lastNum = bcadd($hasNum, $num, 2);
- if ($lastNum > $limitBuy) {
- // 如果有特价,则超过的数量按原价卖;否则中断
- if ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0) {
- Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $lastNum]);
- return;
- }
- util::error(-1, '累计已超出限购数', ['productId'=>$productId, 'limitBuy'=>$limitBuy, 'exceed'=>$lastNum - $limitBuy]);
- }
- if ($doCache) {
- Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $lastNum]);
- }
- } else {
- self::recordLimitBuyRollbackSnapshot($productId, $customId, false, 0);
- if ($num > $limitBuy) {
- // 如果有特价,则超过的数量按原价卖;否则中断
- if ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0) {
- Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $num]);
- return;
- }
- util::error(-1, '超出限购数', ['productId'=>$productId, 'limitBuy'=>$limitBuy, 'exceed'=>$num - $limitBuy]);
- }
- if ($doCache) {
- Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $num]);
- }
- }
- }
- /**
- * 获取零售客户当前清单中各花材的限购情况(Redis 键为 hd_limit_buy:,与批发商 limit_buy_ 区分)
- * @param array $list 花材列表,项含 id 及 bigCount/smallCount 或 bigNum/smallNum
- * @param int|string $customId 零售端 xhCustom.id(花店客户)
- */
- public static function getLimitBuyInfoByList($list, $customId)
- {
- if (empty($list) || !is_array($list)) {
- return [];
- }
- $buyNumMap = [];
- $ids = [];
- foreach ($list as $item) {
- $productId = $item['id'] ?? 0;
- if (empty($productId)) {
- continue;
- }
- $productId = intval($productId);
- $bigNum = $item['bigCount'] ?? ($item['bigNum'] ?? 0);
- $smallNum = $item['smallCount'] ?? ($item['smallNum'] ?? 0);
- if (isset($buyNumMap[$productId]) == false) {
- $buyNumMap[$productId] = [
- 'bigNum' => 0,
- 'smallNum' => 0,
- ];
- }
- $buyNumMap[$productId]['bigNum'] = bcadd($buyNumMap[$productId]['bigNum'], $bigNum, 2);
- $buyNumMap[$productId]['smallNum'] = bcadd($buyNumMap[$productId]['smallNum'], $smallNum, 2);
- $ids[] = $productId;
- }
- $ids = array_values(array_unique($ids));
- if (empty($ids)) {
- return [];
- }
- $productData = \bizGhs\product\classes\ProductClass::getProductByIds($ids);
- $productData = array_column($productData, null, 'id');
- $respond = [];
- foreach ($ids as $productId) {
- $product = $productData[$productId] ?? [];
- if (empty($product)) {
- continue;
- }
- $limitBuy = $product['limitBuy'] ?? 0;
- $isLimit = $limitBuy > 0 ? true : false;
- $hasBuyNum = 0;
- if ($isLimit) {
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- $hasBuyNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
- $hasBuyNum = $hasBuyNum == null ? 0 : $hasBuyNum;
- }
- $buyNum = $buyNumMap[$productId] ?? [];
- $ratio = $product['ratio'] ?? 1;
- $currentBuyNum = \bizGhs\product\classes\ProductClass::mergeItemNum($buyNum['bigNum'] ?? 0, $buyNum['smallNum'] ?? 0, $ratio);
- $totalBuyNum = bcadd($hasBuyNum, $currentBuyNum, 2);
- $specialPrice = (
- ($product['hjDiscountPrice'] ?? 0) > 0
- && ($product['discountPrice'] ?? 0) > 0
- && ($product['skDiscountPrice'] ?? 0) > 0
- ) ? true : false;
- $respond[] = [
- 'id' => $productId,
- 'name' => $product['name'] ?? '',
- 'isLimit' => $isLimit,
- 'limitBuy' => (float)$limitBuy,
- 'specialPrice' => $specialPrice,
- 'hasBuyNum' => $isLimit ? (float)$hasBuyNum : 0,
- 'currentBuyNum' => (float)$currentBuyNum,
- 'reachLimitBuyNum' => ($isLimit && $totalBuyNum > $limitBuy) ? true : false,
- 'exceedNum' => $isLimit ? (float)bcsub($hasBuyNum, $limitBuy, 2) : 0,
- ];
- }
- return $respond;
- }
- //清空限购的缓存
- public static function clearLimitBuyCache($productId, $clearAll = true)
- {
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- $arr = Yii::$app->redis->executeCommand('HKEYS', [$limitKey]);
- if (!empty($arr)) {
- foreach ($arr as $field) {
- self::baseClearLimitBuy($productId, $field);
- }
- }
- if ($clearAll === true) {
- self::clearLimitBuyClearMark($productId);
- self::clearLimitBuyClearLoopConfig($productId);
- }
- }
- public static function getHasLimitBuyList($product)
- {
- $productId = $product->id;
- $limitBuy = $product->limitBuy ?? 0;
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- $arr = Yii::$app->redis->executeCommand('HKEYS', [$limitKey]);
- $customList = [];
- if (!empty($arr)) {
- $numMap = [];
- $ids = [];
- foreach ($arr as $customId) {
- $num = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
- $numMap[$customId] = $num;
- $ids[] = $customId;
- }
- $specialPrice = ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0); // 销售花材是否开启特价
- $customList = CustomClass::getAllByCondition(['id' => ['in', $ids]], null, '*');
- foreach ($customList as $key => $val) {
- $customId = $val['id'];
- $num = $numMap[$customId] ?? 0;
- $customList[$key]['hasLimitBuyNum'] = $num;
- $customList[$key]['reachLimitBuyNum'] = $num >= $limitBuy ? 1 : 0;
- $avatar = $val['avatar'] ?? '';
- $smallAvatar = imgUtil::groupImg($avatar);
- $customList[$key]['smallAvatar'] = $smallAvatar . "?x-oss-process=image/resize,m_fill,h_80,w_80";
- $customList[$key]['specialPrice'] = $specialPrice;
- }
- }
- return $customList;
- }
- /**
- * @param $productId
- * @param $customId
- * @param int $num -1 表示减全部,大于-1表示减值
- */
- public static function baseClearLimitBuy($productId, $customId, $num = -1)
- {
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- if ($num <= -1) {
- //清掉全部
- Yii::$app->redis->executeCommand('HDEL', [$limitKey, $customId]);
- } else {
- $hasNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
- $remainNum = $hasNum > $num ? bcsub($hasNum, $num) : 0;
- $remainNum = floor($remainNum);
- if ($remainNum > 0) {
- Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $remainNum]);
- } else {
- Yii::$app->redis->executeCommand('HDEL', [$limitKey, $customId]);
- }
- }
- }
- // 只清空已购买记录,不关闭商品限购配置
- public static function clearLimitBuyRecordByProductId($productId)
- {
- $productId = intval($productId);
- if ($productId <= 0) {
- return false;
- }
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- Yii::$app->redis->executeCommand('DEL', [$limitKey]);
- return true;
- }
- // 校验清空限购消息是否是当前有效版本
- public static function checkLimitBuyClearMessage($productId, $clearAt)
- {
- $productId = intval($productId);
- $clearAt = intval($clearAt);
- if ($productId <= 0 || $clearAt <= 0) {
- return false;
- }
- $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
- $currentClearAt = intval(Yii::$app->redis->executeCommand('GET', [$clearMarkKey]));
- return $currentClearAt > 0 && $currentClearAt == $clearAt;
- }
- // 清理限购清空标记
- public static function clearLimitBuyClearMark($productId)
- {
- $productId = intval($productId);
- if ($productId <= 0) {
- return false;
- }
- $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
- Yii::$app->redis->executeCommand('DEL', [$clearMarkKey]);
- return true;
- }
- /**
- * 创建限购缓存(有过期时间)
- * @param $productId 花材ID
- * @param int $seconds 过期时间
- * @param array $options ['clearAt' => 0, 'recurring' => 0, 'intervalDays' => 0, 'clearHour' => 0] 循环清空配置
- * @return bool
- */
- public static function createLimitBuyCache($productId, $seconds = 0, $options = [])
- {
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
- $clearLoopKey = self::CLEAR_LOOP_KEY . $productId;
- $recurring = !empty($options['recurring']);
- $has = Yii::$app->redis->executeCommand('HEXISTS', [$limitKey, 0]);
- if (!empty($has)) {
- // 获取缓存超时时间
- $expire = Yii::$app->redis->executeCommand('TTL', [$limitKey]);
- if ($expire > 0) {
- //return;
- }
- //删除缓存
- Yii::$app->redis->executeCommand('HDEL', [$limitKey, 0]);
- }
- Yii::$app->redis->executeCommand('HSET', [$limitKey, 0, 0]);
- if ($seconds > 0) {
- $clearAt = intval($options['clearAt'] ?? 0);
- if ($clearAt <= 0) {
- $clearAt = time() + intval($seconds);
- }
- Yii::$app->redis->executeCommand('EXPIRE', [$limitKey, $seconds]);
- Yii::$app->redis->executeCommand('SET', [$clearMarkKey, $clearAt]);
- if ($recurring) {
- $intervalDays = intval($options['intervalDays'] ?? 0);
- $clearHour = intval($options['clearHour'] ?? -1);
- Yii::$app->redis->executeCommand('SET', [$clearLoopKey, json_encode([
- 'intervalDays' => $intervalDays,
- 'clearHour' => $clearHour,
- ], JSON_UNESCAPED_UNICODE)]);
- } else {
- Yii::$app->redis->executeCommand('DEL', [$clearLoopKey]);
- }
- // 用 RabbitMQ 延迟消息在到期时清空订单项限购字段
- $message = [
- 'type' => 'limit_buy_clear',
- 'ptType' => 'hd',
- 'productId' => $productId,
- 'clearAt' => $clearAt,
- ];
- if ($recurring) {
- $message['recurring'] = 1;
- $message['intervalDays'] = intval($options['intervalDays'] ?? 0);
- $message['clearHour'] = intval($options['clearHour'] ?? -1);
- }
- $message = serialize($message);
- $producer = Yii::$app->rabbitmq->getProducer('stockProducer');
- $producer->publish($message, 'limitBuyDelayExchange', 'limitBuyDelayRoute', [
- 'delivery_mode' => 2,
- 'content_type' => 'application/octet-stream',
- 'application_headers' => new AMQPTable([
- 'x-delay' => intval($seconds * 1000),
- ]),
- ]);
- Yii::info('限购延迟消息已发送: ' . json_encode([
- 'type' => 'limit_buy_clear',
- 'ptType' => 'hd',
- 'productId' => intval($productId),
- 'clearAt' => intval($clearAt),
- 'delayMs' => intval($seconds * 1000),
- 'recurring' => $recurring ? 1 : 0,
- ], JSON_UNESCAPED_UNICODE), __METHOD__);
- } else {
- Yii::$app->redis->executeCommand('DEL', [$clearMarkKey]);
- Yii::$app->redis->executeCommand('DEL', [$clearLoopKey]);
- }
- }
- // 创建循环清空限购缓存
- public static function createRecurringLimitBuyCache($productId, $intervalDays, $clearHour, $clearAt = 0)
- {
- $productId = intval($productId);
- $intervalDays = intval($intervalDays);
- $clearHour = intval($clearHour);
- if ($productId <= 0 || $intervalDays <= 0 || $clearHour < 0 || $clearHour > 23) {
- return false;
- }
- if ($clearAt <= 0) {
- $clearAt = self::getNextLimitBuyClearAt($intervalDays, $clearHour);
- }
- $seconds = max(1, $clearAt - time());
- self::createLimitBuyCache($productId, $seconds, [
- 'recurring' => 1,
- 'intervalDays' => $intervalDays,
- 'clearHour' => $clearHour,
- 'clearAt' => $clearAt,
- ]);
- return true;
- }
- // 计算下一次循环清空时间
- public static function getNextLimitBuyClearAt($intervalDays, $clearHour, $baseTime = 0)
- {
- $baseTime = $baseTime > 0 ? intval($baseTime) : time();
- $targetDay = $baseTime + intval($intervalDays) * 86400;
- $clearAt = strtotime(date('Y-m-d', $targetDay) . ' ' . sprintf('%02d:00:00', intval($clearHour)));
- while ($clearAt <= $baseTime) {
- $clearAt = strtotime('+' . intval($intervalDays) . ' days', $clearAt);
- }
- return $clearAt;
- }
- // 获取循环清空限购配置
- public static function getLimitBuyClearLoopConfigByProductId($productId)
- {
- $productId = intval($productId);
- if ($productId <= 0) {
- return [];
- }
- $clearLoopKey = self::CLEAR_LOOP_KEY . $productId;
- $config = Yii::$app->redis->executeCommand('GET', [$clearLoopKey]);
- if (empty($config)) {
- return [];
- }
- $config = json_decode($config, true);
- return is_array($config) ? $config : [];
- }
- // 获取限购清空配置,用于商品详情回显
- public static function getLimitBuyClearInfo($productId)
- {
- $productId = intval($productId);
- $info = [
- 'limitBuyClearType' => 0, // 0不清空 1循环清空 2定时清空
- 'limitBuyClearTypeName' => '不清空',
- 'limitBuyClearAt' => 0,
- 'limitBuyClearTime' => '',
- 'limitBuyNextClearAt' => 0,
- 'limitBuyNextClearTime' => '',
- 'limitBuyClearIntervalDays' => 0,
- 'cleartAt' => '',
- ];
- if ($productId <= 0) {
- return $info;
- }
- $loopConfig = self::getLimitBuyClearLoopConfigByProductId($productId);
- if (!empty($loopConfig)) {
- $info['limitBuyClearType'] = 1;
- $info['limitBuyClearTypeName'] = '循环清空';
- $info['limitBuyClearIntervalDays'] = intval($loopConfig['intervalDays'] ?? 0);
- $info['cleartAt'] = intval($loopConfig['clearHour'] ?? 0);
- return $info;
- }
- $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
- $clearAt = intval(Yii::$app->redis->executeCommand('GET', [$clearMarkKey]));
- if ($clearAt <= 0) {
- return $info;
- }
- $info['limitBuyClearType'] = 2;
- $info['limitBuyClearTypeName'] = '定时清空';
- $info['limitBuyClearAt'] = $clearAt;
- $info['limitBuyClearTime'] = date('Y-m-d H:i:s', $clearAt);
- $info['limitBuyNextClearAt'] = $clearAt;
- $info['limitBuyNextClearTime'] = $info['limitBuyClearTime'];
- return $info;
- }
- // 清理循环清空限购配置
- public static function clearLimitBuyClearLoopConfig($productId)
- {
- $productId = intval($productId);
- if ($productId <= 0) {
- return false;
- }
- $clearLoopKey = self::CLEAR_LOOP_KEY . $productId;
- Yii::$app->redis->executeCommand('DEL', [$clearLoopKey]);
- return true;
- }
- /**
- * 记录限购缓存写入前快照(同一次请求内同商品同客户只记录一次)
- *
- * @param int $productId
- * @param int $customId 零售xhCustom表的id
- * @param bool $hasOldValue
- * @param string|int|float $oldValue
- * @return void
- */
- public static function recordLimitBuyRollbackSnapshot($productId, $customId, $hasOldValue, $oldValue = 0)
- {
- $productId = intval($productId);
- $customId = intval($customId);
- if ($productId <= 0 || $customId <= 0) {
- return;
- }
- $snapshotKey = $productId . '_' . $customId;
- if (!isset(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY]) || !is_array(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY])) {
- Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY] = [];
- }
- if (isset(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY][$snapshotKey])) {
- return;
- }
- Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY][$snapshotKey] = [
- 'productId' => $productId,
- 'customId' => $customId,
- 'hasOldValue' => $hasOldValue ? 1 : 0,
- 'oldValue' => strval($oldValue),
- ];
- }
- /**
- * 根据花材清空订单项限购值
- *
- * @param int $productId
- * @return bool
- */
- public static function clearLimitBuyByProductId($productId)
- {
- $productId = intval($productId);
- if ($productId <= 0) {
- return false;
- }
- self::updateById($productId, ['limitBuy' => 0]);
- return true;
- }
- /**
- * 回滚当前请求中已记录的限购缓存变更
- *
- * @return bool
- */
- public static function rollbackLimitBuySnapshot()
- {
- $snapshotList = Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY] ?? [];
- if (empty($snapshotList) || !is_array($snapshotList)) {
- return true;
- }
- foreach ($snapshotList as $snapshot) {
- $productId = intval($snapshot['productId'] ?? 0);
- $customId = intval($snapshot['customId'] ?? 0);
- if ($productId <= 0 || $customId <= 0) {
- continue;
- }
- $limitKey = self::LIMIT_BUY_KEY . $productId;
- $hasOldValue = intval($snapshot['hasOldValue'] ?? 0);
- if ($hasOldValue == 1) {
- $oldValue = $snapshot['oldValue'] ?? '0';
- Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $oldValue]);
- } else {
- Yii::$app->redis->executeCommand('HDEL', [$limitKey, $customId]);
- }
- }
- self::clearLimitBuyRollbackSnapshot();
- return true;
- }
- /**
- * 清理当前请求中记录的限购回滚快照
- *
- * @return void
- */
- public static function clearLimitBuyRollbackSnapshot()
- {
- unset(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY]);
- }
- }
|