id ?? 0; $ptItemId = $respond->itemId ?? 0; $cost = $respond->cost ?? 0; $name = $respond->name ?? ''; $cover = $respond->cover ?? ''; $staffId = $data['staffId'] ?? 0; $staffName = $data['staffName'] ?? ''; if (isset($respond->hasCpId) == false || $respond->hasCpId == 0) { //noticeUtil::push("mainId:" . $mainId . "建的花材【{$name}】没有关联产品", '15280215347'); } $ptItem = PtItemClass::getById($ptItemId, true); if (!empty($ptItem)) { $ptItem->useNum = 1; $ptItem->save(); } if ($cost > 0) { $changeData = [ 'ptStyle' => 2, 'sjId' => $sjId, 'mainId' => $mainId, 'itemId' => $productId, 'ptItemId' => $ptItemId, 'cost' => $cost, 'staffId' => $staffId, 'staffName' => $staffName, 'name' => $name ?? '', 'cover' => $cover, 'type' => 3, 'targetId' => $productId ?? 0, 'event' => '创建花材录入', ]; CostChangeClass::addData($changeData); } $price = $data['price'] ?? 0; $skPrice = $data['skPrice'] ?? 0; if ($price > 0) { $changeData = [ 'ptStyle' => 2, 'sjId' => $sjId, 'mainId' => $mainId, 'itemId' => $productId, 'ptItemId' => $ptItemId, 'price' => $price, 'skPrice' => $skPrice, 'staffId' => $staffId, 'staffName' => $staffName, 'name' => $name, 'cover' => $cover, 'event' => '创建花材录入', 'type' => 3, 'targetId' => $productId, ]; PriceChangeClass::addData($changeData); } return $respond; } //根据itemIds获取相应的花材信息 public static function getProductInfoByItemIds(array $itemIds, $hdMainId) { $where = ['itemId' => ['in', $itemIds], 'delStatus' => 0, 'mainId' => $hdMainId]; $data = self::getAllList('*', $where); $data = self::groupItemBaseInfo($data); return $data; } //根据花材ID,获取花材名称,图片 public static function groupItemBaseInfo($list) { //取得itemIds $itemIds = array_column($list, 'itemId'); //获取相应的图片,名称,py $where = ['id' => ['in', $itemIds]]; $data = xhItemService::getAllByCondition($where, null, '*'); if (!empty($data)) { $data = array_column($data, NULL, 'id'); foreach ($list as $k => $v) { if (isset($data[$v['itemId']]['cover']) && !empty($data[$v['itemId']]['cover'])) { $cover = self::groupImg($data[$v['itemId']]['cover']); } else { //没有图片时取默认图片 ssh 2021.2.18 $cover = imgUtil::getPrefix() . '/hhb_small.png'; } $unitNum = $data[$v['itemId']]['ratio'] ?? 0; $stockInfo = self::formatStock($v['stock'], $unitNum); $list[$k]['cover'] = $cover; $list[$k]['bigNum'] = $stockInfo['bigNum']; $list[$k]['smallNum'] = $stockInfo['smallNum']; $list[$k]['itemName'] = $data[$v['itemId']]['name'] ?? ''; $list[$k]['py'] = $data[$v['itemId']]['py'] ?? ''; $list[$k]['ratio'] = $unitNum ?? 0; //换算大小单位对应价格 $list[$k]['bigPrice'] = $v['price']; //大单位价格就是product表存的价格 $smallPrice = 0; if ($unitNum) { $smallPrice = bcdiv($v['price'], $unitNum, 2); } $list[$k]['smallPrice'] = $smallPrice; $list[$k]['bigUnit'] = $data[$v['itemId']]['bigUnit']; $list[$k]['smallUnit'] = $data[$v['itemId']]['smallUnit']; } } return $list; } //获取详情 ruidian 2021.5.3 public static function getItemInfo($id, $ghs = []) { $info = self::getById($id); if (empty($info)) { return $info; } $level = $ghs->giveLevel ?? 1; $list = self::groupProductInfo([$info], $level); return current($list); } //库存预警列表用到的数据组合 ssh 20221225 public static function warningGroup($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"; } return $list; } /** * 库存预警分页列表(与 get-item-list 共用 where 条件):stock + onStock < stockWarning */ public static function warningList($where, $order = ['inTurn' => SORT_DESC, 'actualSold' => SORT_DESC]) { $get = Yii::$app->request->get(); $page = isset($get['page']) ? intval($get['page']) : 1; $pageSize = isset($get['pageSize']) && !empty($get['pageSize']) ? intval($get['pageSize']) : Yii::$app->params['pageSize']; $offset = ($page - 1) * $pageSize; $model = self::getModel(); $query = $model->conditionQuery($where)->andWhere('(`stock` + `onStock`) < `stockWarning`'); $count = (clone $query)->count(); $totalPage = $pageSize > 0 ? (int)ceil($count / $pageSize) : 0; if (!empty($order)) { $query->orderBy($order); } $list = $query->offset($offset)->limit($pageSize)->asArray()->all(); return [ 'totalNum' => $count, 'totalPage' => $totalPage, 'moreData' => $totalPage > $page ? 1 : 0, 'list' => $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 stockOutGroup($list, $level = 0) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; $addPriceMap = 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 = self::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; } //拆散花材 ssh 20230329 public static function partItemGroup($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; } //报损花材列表 ssh 20221226 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; } //采购花材列表用到的数据组合 ssh 20221225 public static function cgItemGroup($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"; } return $list; } //预订 ssh 20231121 public static function bookItemGroup($list, $level = 0) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; $addPriceMap = 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 = self::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 20221225 public static function kdItemGroup($list, $level = 0, $custom = []) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116 $addPriceMap = CustomClass::$levelAddPriceKeyMap; //但这边加的参数是有用的,给指定客户涨价的 $addPriceMap['risePercent'] = $custom['risePercent'] ?? 0; 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 = self::getFinalPrice($v, $level, $priceMap, $addPriceMap); $list[$k]['originPrice'] = $list[$k]['price']; //原始价 $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 20221225 public static function itemListGroup($list, $level = 0) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; $addPriceMap = 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 = self::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 cgStaffListGroup($list, $level = 0) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; $addPriceMap = 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 = self::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 changeStockGroup($list, $level = 0) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; $addPriceMap = 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 = self::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 changePriceGroup($list, $level = 0) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; $addPriceMap = 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 = self::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 simpleGroup($list, $level = 0) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; $addPriceMap = 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 = self::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; } return $list; } //通用花材列表数组组合 public static function groupProductInfo($list, $level = 0, $custom = []) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116 $addPriceMap = CustomClass::$levelAddPriceKeyMap; //但这边加的参数是有用的,给指定客户涨价的 $addPriceMap['risePercent'] = $custom['risePercent'] ?? 0; foreach ($list as $k => $v) { $shortCover = $v['cover'] ?? ''; $cover = imgUtil::getPrefix() . 'hhb_small.png'; $bigCover = imgUtil::getPrefix() . 'hhb_small.png'; $largeCover = 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); } $bigCover = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_700,w_700"; $largeCover = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_990,w_990"; } $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1; $stockInfo = self::formatStock($v['stock'], $ratio); $list[$k]['cover'] = $cover; $list[$k]['bigCover'] = $bigCover; $list[$k]['largeCover'] = $largeCover; $list[$k]['shortCover'] = $shortCover; $list[$k]['bigNum'] = $stockInfo['bigNum']; $list[$k]['smallNum'] = $stockInfo['smallNum']; $list[$k]['itemName'] = $v['name'] ?? ''; $list[$k]['prePrice'] = $v['price'] ?? 0; //今日特价、会员价 $price = self::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; $preSellDate = $v['presellDate'] ?? ''; $preSellDateList = []; $preSellDateShow = []; if (!empty($preSellDate)) { $dateArr = explode(',', trim($preSellDate)); if (!empty($dateArr)) { foreach ($dateArr as $sellTime) { $sellDate = date("Y-m-d", $sellTime); $showDate = date("n.j", $sellTime); $preSellDateList[] = $sellDate; $preSellDateShow[] = $showDate; } } } $list[$k]['presellDateList'] = $preSellDateList; $list[$k]['presellDateShow'] = $preSellDateShow; //这个很重要不能随意乱改 $autoPrice = bcadd($v['cost'], $v['addPrice'], 2); $skMore = $v['skMore'] ?? 0; $skAutoPrice = bcadd($autoPrice, $skMore, 2); $list[$k]['autoPrice'] = $autoPrice; $list[$k]['autoSkPrice'] = $skAutoPrice; $list[$k]['userPrice'] = 0; // 限购缓存 if ($v['limitBuy'] > 0) { $limitKey = self::LIMIT_BUY_KEY . $v['id']; $has = Yii::$app->redis->executeCommand('HEXISTS', [$limitKey, 0]); if (!empty($has)) { // 获取缓存超时时间 $expire = Yii::$app->redis->executeCommand('TTL', [$limitKey]); if ($expire > 0) { $list[$k]['limitBuyClearTime'] = date('Y-m-d H:i:s', time() + $expire); } else { $list[$k]['limitBuyClearTime'] = ''; } } else { $list[$k]['limitBuyClearTime'] = ''; } } } return $list; } //零售端花店采购 public static function hdCgGroup($list, $level = 0, $ghs = []) { //各个等级对应的price addPrice字段 $priceMap = CustomClass::$levelPriceKeyMap; //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116 $addPriceMap = CustomClass::$levelAddPriceKeyMap; //这里加的参数是有用的 ssh 20260116 $addPriceMap['risePercent'] = $ghs['risePercent'] ?? 0; 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; $stockInfo = self::formatStock($v['stock'], $ratio); $list[$k]['cover'] = $cover; $list[$k]['prePrice'] = $v['price'] ?? 0; $list[$k]['bigNum'] = $stockInfo['bigNum']; $list[$k]['smallNum'] = $stockInfo['smallNum']; $list[$k]['itemName'] = $v['name'] ?? ''; //今日特价、会员价 $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap); $defaultGradePrice = 0; if ($level == 2) { //如果是大客户,取出更低一级的客户价格,用于前端对比展示:显示原价和会员价 $littleLevel = 1; $defaultGradePrice = self::getFinalPrice($v, $littleLevel, $priceMap, $addPriceMap); } $list[$k]['defaultGradePrice'] = $defaultGradePrice; $list[$k]['myLevel'] = $level; $reachPrice = 0; //满10扎多少钱 if (isset($v['reachNum']) && $v['reachNum'] > 0) { $reachNumDiscount = $v['reachNumDiscount'] ?? 0; $reachPrice = bcsub($price, $reachNumDiscount, 2); $reachPrice = $reachPrice <= 0 ? 0.01 : floatval($reachPrice); } $list[$k]['reachPrice'] = $reachPrice; $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; $preSellDate = $v['presellDate'] ?? ''; $preSellDateShow = []; if (!empty($preSellDate)) { $dateArr = explode(',', trim($preSellDate)); if (!empty($dateArr)) { foreach ($dateArr as $sellTime) { $showDate = date("n.j", $sellTime); $preSellDateShow[] = $showDate; } } } $list[$k]['presellDateShow'] = $preSellDateShow; unset($list[$k]['presellDate']); unset($list[$k]['addPrice']); unset($list[$k]['cost']); unset($list[$k]['skMore']); } return $list; } /** * 输出价格 ssh 20211009 * @param $product 花材信息 * @param $level 等级 * @param $priceMap 各个等级对应的price addPrice字段 * @param $params 原来是 $addPriceMap,现在 $addPriceMap 值没用了,把它改成 $params ,用作传别的参数 * @param int $changePrice 开单改价 * @param int $buyNum 当前购买数量 * @param int $totalBuyNum 已购买总数量 * @return int|string|null * @throws \Exception */ public static function getFinalPrice($product, $level, $priceMap, $params, $changePrice = 0, $buyNum = 0, $totalBuyNum = 0) { //开单改价优先级最高 if ($changePrice > 0) { return $changePrice; } if (empty($product) || isset($product['price']) == false) { return 0; } if ($level == 9) { $avCost = $product['avCost'] ?? 0; $price = $avCost; if ($price <= 0) { $cost = $product['cost'] ?? 0; $price = $cost; } } else { $currentPriceField = $priceMap[$level] ?? 'price'; $price = $product[$currentPriceField] ?? 0; } //特价与限购的综合处理 $discountPrice = $product['discountPrice'] ?? 0; $hjDiscountPrice = $product['hjDiscountPrice'] ?? 0; $skDiscountPrice = $product['skDiscountPrice'] ?? 0; $limitBuy = $product['limitBuy']; if ($discountPrice > 0) { $oldPrice = $price; if ($level == 0) { $price = $skDiscountPrice; } if ($level == 1) { $price = $discountPrice; } if ($level == 2) { $price = $hjDiscountPrice; } //超出限购情况的价格计算 if ($limitBuy > 0 && $totalBuyNum > $limitBuy) { $hasBuyNum = $totalBuyNum - $buyNum; if ($hasBuyNum >= $limitBuy) { $price = $oldPrice; } else { $overBuyNum = $totalBuyNum - $limitBuy; $overBuyPrice = bcmul($overBuyNum, $oldPrice, 2); // 超出限购部分的价格 $leftBuyNum = $buyNum - $overBuyNum; $leftBuyPrice = bcmul($leftBuyNum, $price, 2); // 未超出限购部分的价格 $price = bcdiv($overBuyPrice + $leftBuyPrice, $buyNum, 2); // 最终价格 } } } //满多少数量,再优惠多少钱 $reachNum = $product['reachNum'] ?? 0; $reachNumDiscount = $product['reachNumDiscount'] ?? 0; if ($reachNum > 0) { if ($buyNum >= $reachNum) { if ($reachNumDiscount >= $price) { util::fail('优惠价比售价还大'); } $price = bcsub($price, $reachNumDiscount, 2); } } //这个 $risePercent 可能是正整数,也可能是负整数,如果是10表示$price涨价10%,如果是-10表示降价10%,同时$price降价后,不能小于0.01 $risePercent = $params['risePercent'] ?? 0; if ($risePercent != 0) { // risePercent为正数表示涨价百分比,负数表示降价百分比 $priceAdjustment = bcmul($price, bcdiv($risePercent, 100, 4), 2); $price = bcadd($price, $priceAdjustment, 2); // 降价后不能小于0.01 if ($price < 0.01) { $price = 0.01; } } return $price; } //根据花材ID,库存=》 计算出多少扎,多少支 public static function formatStockByItemId($itemId, $stock) { $unitNum = xhItemService::getItemUnitNum($itemId); return self::formatStock($stock, $unitNum); } //花材的库存转换: 库存是小数,根据各个花材的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, ]; } //根据 花材ID,将花材大小数量 换算成小数 public static function mergeItemNumByItemId($bigNum, $smallNum, $itemId) { $unitNum = xhItemService::getItemUnitNum($itemId); return self::mergeItemNum($bigNum, $smallNum, $unitNum); } //花材扎、支 合并转换 小数 /** * @param $bigNum 大单位 数量 * @param $smallNum 小单位 数量 * @param $unitNum 花材比例 * @return string */ public static function mergeItemNum($bigNum, $smallNum, $unitNum) { $stockDecimal = 0; if ($unitNum > 0) { $stockDecimal = bcdiv($smallNum, $unitNum, 2); } $itemNum = bcadd($bigNum, $stockDecimal, 2); return $itemNum; } //获取常用的itemIds public static function getOftenItemIds($shopId) { $data = self::getLimitList("itemId", ['shopId' => $shopId], 10, ['actualSold' => SORT_DESC, 'inTurn' => SORT_DESC]); if ($data) { $data = array_column($data, 'itemId'); } return $data; } /** * 库存变动统一入口:用数据库行锁串行化同一花材的读-算-写,避免并发覆盖库存。 * 出现并发时,会自动重试3次。 * @param $productId * @param callable $callback * @return mixed * @throws \Exception */ private static function runStockMutation($productId, callable $callback) { $db = Yii::$app->db; $currentTransaction = $db->getTransaction(); $ownTransaction = empty($currentTransaction) || !$currentTransaction->isActive; $mutation = function () use ($db, $currentTransaction, $ownTransaction, $productId, $callback) { $transaction = $ownTransaction ? $db->beginTransaction() : $currentTransaction; try { $productData = self::getLockById($productId); if (!$productData) { util::fail("花材不存在"); } $respond = call_user_func($callback, $productData); if ($ownTransaction) { $transaction->commit(); } return $respond; } catch (\Exception $exception) { if ($ownTransaction && !empty($transaction) && $transaction->isActive) { $transaction->rollBack(); } throw $exception; } }; if (!$ownTransaction) { return call_user_func($mutation); } return util::runWithDbConcurrencyRetry($mutation); } //加库存,允许负库存,负数数量(盘点时) public static function addStock($productId, $itemNumBundle, $itemNumPiece) { //小单位库存不再考虑 ssh 20230303 //$itemNumPiece = 0; $callback = function ($productData) use ($itemNumBundle, $itemNumPiece) { $oldStock = $productData->stock; $ratio = $productData->ratio; //转成小数,加库存 $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio); $newStock = bcadd($oldStock, $itemNum, 2); $productData->stock = $newStock; //上架 if ($newStock > 0 && $productData->status == 2) { $productData->status = 1; } $productData->save(); return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum]; }; return self::runStockMutation($productId, $callback); } //加路上库存 public static function addOnStockByItemNum($productId, $itemNum) { $callback = function ($productInfo) use ($itemNum) { $oldOnStock = $productInfo->onStock; $newOnStock = bcadd($oldOnStock, $itemNum); $productInfo->onStock = $newOnStock; $productInfo->save(); return ['oldOnStock' => $oldOnStock, 'newOnStock' => $newOnStock, 'itemNum' => $itemNum]; }; return self::runStockMutation($productId, $callback); } //加库存 public static function addStockByItemNum($productId, $itemNum, $params = []) { $callback = function ($productData) use ($itemNum, $params) { $oldStock = $productData->stock; $newItemCost = 0; $newItemStock = 0; $avCost = 0; $changeCost = 0; $unitCost = 0; //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost if (!empty($params['cost']) && $params['cost'] > 0) { //原库存和原成本,也叫剩余库存和剩余成本 $oldItemStock = $productData->stock ?? 0; $oldAvCost = $productData->avCost ?? 0; $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2); //变动库存和成本 $unitCost = $params['cost']; $changeCost = bcmul($params['cost'], $itemNum, 2); //新库存和新成本 $newItemCost = bcadd($changeCost, $oldItemCost, 2); $newItemStock = bcadd($itemNum, $oldItemStock, 2); if ($newItemStock > 0) { $avCost = bcdiv($newItemCost, $newItemStock, 2); } else { $newItemStock = 0; $newItemCost = 0; } $productData->avCost = $avCost; $productData->totalCost = $newItemCost; $productData->totalStock = $newItemStock; } $newStock = bcadd($oldStock, $itemNum, 2); $productData->stock = $newStock; //上架 if ($newStock > 0 && $productData->status == 2) { $productData->status = 1; } //如果花材删除了则要恢复,用于调拨出库时,入库店的花材被删除 if ($newStock > 0 && $productData->delStatus == 1) { $productData->delStatus = 0; } $productData->save(); return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum, 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost]; }; return self::runStockMutation($productId, $callback); } //减库存,允许负库存,负数数量(盘点时) //checkStock boolean 是否需要判断库存 //addSold boolean 是否增加销量,报损和拆散不要增加销量 public static function decreaseStock($productId, $itemNumBundle, $itemNumPiece, $checkStock = false, $addSold = true, $params = []) { //小单位库存不再考虑 ssh 20230303 //$itemNumPiece = 0; $callback = function ($productData) use ($productId, $itemNumBundle, $itemNumPiece, $checkStock, $addSold, $params) { $oldStock = $productData->stock; $ratio = $productData->ratio; //转成小数,更新库存 $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio); $newStock = bcsub($oldStock, $itemNum, 2); $avCost = 0; $newItemStock = 0; $newItemCost = 0; $changeCost = 0; $unitCost = 0; //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost if (!empty($params['cost']) && $params['cost'] > 0) { //变成库存和变成成本 $unitCost = $params['cost']; $changeCost = bcmul($params['cost'], $itemNum, 2); //原库存和原成本,也叫剩余库存和剩余成本 $oldItemStock = $productData->stock; $oldAvCost = $productData->avCost ?? 0; $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2); //新库存和新成本 $newItemCost = bcsub($oldItemCost, $changeCost, 2); $newItemStock = bcsub($oldItemStock, $itemNum, 2); if ($newItemStock > 0) { $avCost = bcdiv($newItemCost, $newItemStock, 2); } else { $newItemStock = 0; $newItemCost = 0; } //如果减库存之后,还有剩余库存,则变更新的平均成本。如果没有库存不再变更平均成本。如没有库存了还变更,会造成全部调拨出库时,保存的成本是0,重要!!!!!!!!!! //确实是,如果没有库存了,平均成本也不能再变化了。如果再变化,平均成本也是变成0。变成0后,如果再盘点几个库存出来,会变成没有成本的库存。 ssh 20251130 if ($newStock > 0) { $productData->avCost = $avCost; } $productData->totalCost = $newItemCost; $productData->totalStock = $newItemStock; } if ($checkStock && $newStock < 0) { //需要判断库存是否充足 $currentName = $productData->name ?? ''; $currentRemainStock = floatval($oldStock); util::fail("{$currentName} 只剩{$currentRemainStock}{$productData->bigUnit}"); } $productData->stock = $newStock; if ($addSold) { //减库存,加销量 2021.6.21 lqh $actualSold = $productData->actualSold; $productData->actualSold = bcadd($actualSold, $itemNum, 2); } //如果花材是自动补充库存类型的花材,则库存低于900时,自动补充到98520,多处要同步修改,关键词 auto_add_stock if ($productData->virtualStock == 1 && $newStock < 9900) { $productData->stock = 98520; } //库存=0 下架商品,特价去掉,限购去掉 lqh 2021.7.9 ssh 20251106 花材满减去掉 20260318 if ($newStock <= 0) { $productData->status = 2; //限购数量重置为 0,但会把限购不为 0 的数据记录到缓存 self::createRecoverLimitBuyCache($productId, $productData->limitBuy); } $productData->save(); return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum, 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost]; }; return self::runStockMutation($productId, $callback); } //根据数量减库存 public static function decreaseStockByItemNum($productId, $itemNum, $checkStock = false) { $callback = function ($productData) use ($itemNum, $checkStock) { $oldStock = $productData->stock; $newStock = bcsub($oldStock, $itemNum, 2); if ($checkStock && $newStock < 0) { //需要判断库存是否充足 util::fail("库存不足"); } $productData->stock = $newStock; //减库存,加销量 2021.6.21 lqh $actualSold = $productData->actualSold; $productData->actualSold = bcadd($actualSold, $itemNum, 2); $productData->save(); return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum]; }; return self::runStockMutation($productId, $callback); } //减少在路上的库存 public static function decreaseOnStockByItemNum($productId, $itemNum, $checkStock = false) { $callback = function ($productInfo) use ($itemNum, $checkStock) { $oldOnStock = $productInfo->onStock; $newOnStock = bcsub($oldOnStock, $itemNum); if ($checkStock && $newOnStock < 0) { //需要判断库存是否充足 util::fail("库存不足"); } $productInfo->onStock = $newOnStock; $productInfo->save(); return ['oldOnStock' => $oldOnStock, 'newOnStock' => $newOnStock, 'itemNum' => $itemNum]; }; return self::runStockMutation($productId, $callback); } //计算价格 public static function computedPrice($productId, $bigNum, $smallNum) { //获取itemId的价格 $productData = self::getById($productId); if (!$productData) { util::fail("花材不存在"); } $price = $productData['price']; $itemId = $productData['itemId']; $unitNum = xhItemService::getItemUnitNum($itemId); //换算出一支多少钱 $piecePrice = 0; if ($unitNum > 0) { //有支 $unitPrice = bcdiv($price, $unitNum, 2); $piecePrice = bcmul($unitPrice, $smallNum, 2); } $bundlePrice = bcmul($price, $bigNum, 2); $total = bcadd($bundlePrice, $piecePrice, 2); return $total; } //根据门店ID, 平台花材ID, 获取门店的花材ID public static function getGhsProductId($shopId, $itemId) { $data = self::getGhsProductDataByItemId($shopId, $itemId); if ($data) { return $data['id']; } return 0; } public static function getGhsProductDataByItemId($shopId, $itemId) { $where = ['shopId' => $shopId, 'itemId' => $itemId]; $data = self::getByCondition($where); return $data; } //根据多个itemIds shopId 获取对应的 productId lqh 2021.1.24 public static function getGhsProductDataByItemIds($mainId, $itemIds) { $where = ['itemId' => ['in', $itemIds], 'mainId' => $mainId]; $data = self::getAllByCondition($where, null, "*"); return $data; } //补全itemId (针对入库时 当前门店下无对应的花材 itemId) lqh 2021.1.24 public static function complementProduct($sjId, $mainId, $shopId, $itemInfo) { $itemIds = array_column($itemInfo, "itemId"); $oldProductIds = array_column($itemInfo, "productId"); $productData = self::getGhsProductDataByItemIds($mainId, $itemIds); $defaultClass = ItemClassClass::getByCondition(['mainId' => $mainId, 'isDefault' => 1], true); $defaultClassId = $defaultClass->id ?? 0; if (empty($defaultClassId)) { util::fail('没有找到默认分类'); } $shop = ShopClass::getById($shopId, true); if (empty($shop)) { util::fail('没有找到门店24'); } if (count($productData) !== count($itemIds)) { $productDataItemIds = array_column($productData, 'itemId'); //取差集 $diffItemIds = array_diff($itemIds, $productDataItemIds); if ($diffItemIds) { $oldProductInfo = self::getAllByCondition(['id' => ['in', $oldProductIds]], null, '*', 'itemId'); //新增到product表,price = itemPrice (出库时的售价), stock=0 foreach ($diffItemIds as $v) { $addData = []; $addData['sjId'] = $sjId; $addData['shopId'] = $shopId; $addData['mainId'] = $mainId; $addData['itemId'] = $v; $addData['name'] = $oldProductInfo[$v]['name'] ?? ''; $addData['price'] = $oldProductInfo[$v]['price'] ?? 0; $addData['classId'] = $defaultClassId; $addData['smallRatio'] = $oldProductInfo[$v]['smallRatio'] ?? 2; $addData['skPrice'] = $oldProductInfo[$v]['skPrice'] ?? 0; $addData['hjPrice'] = $oldProductInfo[$v]['hjPrice'] ?? 0; $addData['cost'] = $oldProductInfo[$v]['cost'] ?? 0; $addData['addPrice'] = $oldProductInfo[$v]['addPrice'] ?? 0; $addData['skAddPrice'] = $oldProductInfo[$v]['skAddPrice'] ?? 0; $addData['hjAddPrice'] = $oldProductInfo[$v]['hjAddPrice'] ?? 0; $addData['stockWarning'] = $oldProductInfo[$v]['stockWarning'] ?? 5; $addData['weight'] = $oldProductInfo[$v]['weight'] ?? 0; $addData['alias'] = $oldProductInfo[$v]['alias'] ?? ''; $addData['itemRemark'] = $oldProductInfo[$v]['itemRemark'] ?? ''; $addData['py'] = $oldProductInfo[$v]['py'] ?? ''; $addData['ratio'] = $oldProductInfo[$v]['ratio'] ?? 20; $addData['bigUnit'] = $oldProductInfo[$v]['bigUnit'] ?? ''; $addData['smallUnit'] = $oldProductInfo[$v]['smallUnit'] ?? ''; $addData['bigUnitId'] = $oldProductInfo[$v]['bigUnitId'] ?? 0; $addData['smallUnitId'] = $oldProductInfo[$v]['smallUnitId'] ?? 0; $addData['status'] = 1; $addData['delStatus'] = 0; $addData['inTurn'] = 100; self::addProduct($addData, $shop); } } } } public static function check($info, $mainId) { if (empty($info)) { util::fail('没有花材信息'); } if (isset($info->mainId) == false || $info->mainId != $mainId) { util::fail('无效花材'); } return true; } //有效花材判断,包括id有效、同个门店等 ssh 2021.1.19 public static function valid($list, $mainId) { $ids = array_unique(array_filter(array_column($list, 'productId'))); $productList = self::getByIds($ids, null, 'id'); if (!empty($productList)) { foreach ($productList as $product) { if (isset($product['mainId']) == false || $product['mainId'] != $mainId) { $name = $product['name'] ?? ''; $pMainId = $product['mainId'] ?? 0; //noticeUtil::push("只能选择同一家的商品哦!{$name} {$pMainId} {$mainId}", '15280215347'); util::fail($name . ' 不是一家的商品,请清空重选'); } } if (count($productList) != count($ids)) { util::fail('存在无效商品'); } } else { util::fail('商品不存在'); } } //通过ids 批量获取花材商品的信息 public static function getProductByIds($ids) { return self::getByIds($ids); } //修改花材商品的库存: 统一方法 public static function updateStockById($id, $stock, $adminId = 0) { $callback = function ($productData) use ($stock, $adminId) { $productData->stock = $stock; //如果有库存需要保持上架状态 if ($stock > 0) { $productData->status = 1; } if ($adminId) { $productData->adminId = $adminId; } return $productData->save(); }; return self::runStockMutation($id, $callback); } //订单退回库存 public static function backStockByOrderItemInfo($orderItemInfo) { foreach ($orderItemInfo as $v) { self::addStockByItemNum($v['productId'], $v['itemNum']); } } //获取当前门店下商品信息 lqh 2021.1.28 public static function getProductData($id, $mainId, $obj = false) { return self::getByCondition(['id' => $id, 'mainId' => $mainId], $obj); } //单个修改价格 lqh 2021.1.28 public static function changeSinglePrice($shop, $ptItemId, $price, $skPrice = null, $hjPrice = null, $params = []) { $mainId = $shop->mainId ?? 0; $productInfo = self::getByCondition(['itemId' => $ptItemId, 'mainId' => $mainId, 'delStatus' => 0], true); if (empty($productInfo)) { return false; } if (empty($price)) { util::fail('没有批发价'); } $discountPrice = $productInfo->discountPrice ?? 0; $discountPrice = floatval($discountPrice); $name = $productInfo->name ?? ''; if ($discountPrice > 0) { if ($discountPrice >= $price) { //util::fail($name . " 价格应该大于特价"); } } $productInfo->price = $price; if (empty($skPrice)) { util::fail('没有零售价'); } if (empty($hjPrice)) { util::fail('没有会员价'); } $productInfo->skPrice = $skPrice; $productInfo->hjPrice = $hjPrice; $productInfo->save(); $sjId = $shop->sjId ?? 0; $itemId = $productInfo->id ?? 0; $ptItemId = $productInfo->itemId ?? 0; $name = $productInfo->name ?? ''; $cover = $productInfo->cover ?? ''; $staffId = $params['staffId'] ?? 0; $staffName = $params['staffName'] ?? ''; $changeType = $params['changeType'] ?? 'normal'; $targetId = $params['targetId'] ?? 0; $event = $params['event'] ?? '修改'; $type = 0; if ($changeType == 'cg') { $event = '采购后修改'; $type = 1; } $changeData = [ 'ptStyle' => 2, 'sjId' => $sjId, 'mainId' => $mainId, 'itemId' => $itemId, 'ptItemId' => $ptItemId, 'price' => $price, 'skPrice' => $skPrice, 'hjPrice' => $hjPrice, 'staffId' => $staffId, 'staffName' => $staffName, 'name' => $name, 'cover' => $cover, 'event' => $event, 'type' => $type, 'targetId' => $targetId, ]; PriceChangeClass::addData($changeData); return true; } //下单时计算商品的价格 lqh 2021.1.28 public static function formatProductInfo($itemInfo, $level = 0, $live = 1, $moreParams = []) { $data = []; if (empty($itemInfo)) { util::fail('请选择花材,编号 179612'); } $itemInfo = self::mergeItemInfo($itemInfo); $productIds = array_column($itemInfo, 'productId'); $ghsProductData = self::getProductByIds($productIds); $ghsProductData = array_column($ghsProductData, NULL, 'id'); // if ($live == 1) { // $hdProductData = []; // } else { // $hdIds = array_column($itemInfo, 'hdProductId'); // $hdProductData = self::getByIds($hdIds, null, 'id'); // } $totalPrice = 0; $totalBigNum = 0; $totalSmallNum = 0; $book = $moreParams['book'] ?? 0; $customShopAdminId = $moreParams['customShopAdminId'] ?? 0; $custom = $moreParams['custom'] ?? []; $skipLimitBuy = $moreParams['skipLimitBuy'] ?? false; //不同等级对应 price addPrice $priceMap = CustomClass::$levelPriceKeyMap; //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116 $addPriceMap = CustomClass::$levelAddPriceKeyMap; //但这边加的参数是有用的,给指定客户涨价的 $addPriceMap['risePercent'] = $custom['risePercent'] ?? 0; $totalItemNum = 0; //满减累计 $totalReachDiscount = 0; $totalCost = 0; foreach ($itemInfo as $v) { $ghsProductId = $v['productId']; $currentGhsProduct = $ghsProductData[$ghsProductId] ?? []; if (empty($currentGhsProduct)) { noticeUtil::push("ghsProductId={$ghsProductId} 在 ghsProductData 中为空"); continue; } $itemId = $currentGhsProduct['itemId']; $ratio = $currentGhsProduct['ratio']; $cost = $currentGhsProduct['avCost']; $classId = $currentGhsProduct['classId']; $bigNum = $v['bigNum'] ?? 0; $smallNum = $v['smallNum'] ?? 0; //大小数量合并多少扎 $itemNum = self::mergeItemNum($bigNum, $smallNum, $ratio); $totalItemNum = bcadd($totalItemNum, $itemNum, 2); if ($live == 1) { //供货商开单可以传单价过来 $changePrice = isset($v['price']) && $v['price'] > 0 ? $v['price'] : 0; //如果零售店自己下预订单,预付0.1元,有二个地方要同步修改,搜索关键词 pre0.1 if ($book == 1 && $customShopAdminId > 0) { $itemPrice = 0.1; } else { $priceBuyNum = $skipLimitBuy ? 0 : $itemNum; $totalBuyNum = 0; if ($skipLimitBuy == false) { $limitKey = self::LIMIT_BUY_KEY . $ghsProductId; $hasBuyNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $custom['id']]); $totalBuyNum = $itemNum + ($hasBuyNum == null ? 0 : $hasBuyNum); // 已购买总数量 } //今日特价、会员价 $itemPrice = self::getFinalPrice($currentGhsProduct, $level, $priceMap, $addPriceMap, $changePrice, $priceBuyNum, $totalBuyNum); } $thisPrice = $itemPrice; $reachNum = $currentGhsProduct['reachNum'] ?? 0; if ($reachNum > 0) { if ($itemNum >= $reachNum) { $reachNumDiscount = $currentGhsProduct['reachNumDiscount'] ?? 0; $currentReachDiscount = bcmul($reachNumDiscount, $itemNum, 2); $totalReachDiscount = bcadd($totalReachDiscount, $currentReachDiscount, 2); } } } else { $itemPrice = $v['unitPrice'] ?? 0; $thisPrice = $itemPrice; } if ($smallNum > 0) { $price = bcmul($smallNum, $itemPrice, 2); } else { $price = bcmul($bigNum, $itemPrice, 2); } $tmp = []; $tmp['productId'] = $ghsProductId; $tmp['price'] = $price; $tmp['name'] = $currentGhsProduct['name'] ?? ''; $tmp['smallUnit'] = $currentGhsProduct['smallUnit'] ?? '支'; $tmp['bigUnit'] = $currentGhsProduct['bigUnit'] ?? '扎'; $tmp['cover'] = $currentGhsProduct['cover'] ?? ''; $tmp['itemId'] = $itemId; $tmp['rank'] = $currentGhsProduct['rank'] ?? ''; $tmp['ratio'] = $currentGhsProduct['ratio'] ?? 1; $tmp['ratioType'] = $currentGhsProduct['ratioType'] ?? 0; $tmp['variety'] = $currentGhsProduct['variety'] ?? 0; $tmp['unitPrice'] = $thisPrice; $tmp['unitWeight'] = $currentGhsProduct['weight'] ?? 0;; $tmp['num'] = $itemNum; $tmp['bigNum'] = $v['bigNum']; $tmp['smallNum'] = $v['smallNum']; $tmp['preNum'] = $itemNum; $tmp['preBigNum'] = $v['bigNum']; $tmp['preSmallNum'] = $v['smallNum']; $tmp['remark'] = $v['remark'] ?? ''; $tmp['userPrice'] = $price; $tmp['weight'] = $currentGhsProduct['weight'] ?? 0; $tmp['sjId'] = $currentGhsProduct['sjId'] ?? 0; $tmp['shopId'] = $currentGhsProduct['shopId'] ?? 0; $tmp['mainId'] = $currentGhsProduct['mainId'] ?? 0; $tmp['limitBuy'] = $currentGhsProduct['limitBuy']; $tmp['smallUnitPrice'] = $itemPrice; $tmp['classId'] = $classId; $tmp['kind'] = $currentGhsProduct['kind'] ?? 0; $tmp['belongCost'] = $currentGhsProduct['belongCost'] ?? 0; $tmp['hjDiscountPrice'] = $currentGhsProduct['hjDiscountPrice']; $tmp['discountPrice'] = $currentGhsProduct['discountPrice']; $tmp['skDiscountPrice'] = $currentGhsProduct['skDiscountPrice']; $totalPrice = bcadd($totalPrice, $price, 2); $totalBigNum += $bigNum; $totalSmallNum += $smallNum; if ($bigNum > 0) { $xhUnitType = dict::getDict('unitType', 'big'); $xhNum = $bigNum; $xhUnitName = $tmp['bigUnit'] ?? ''; } else { $xhUnitType = dict::getDict('unitType', 'small'); $xhNum = $smallNum; $xhUnitName = $tmp['smallUnit'] ?? ''; $smallCost = bcdiv($cost, $ratio, 2); $cost = $smallCost; } $xhPrice = bcmul($xhNum, $itemPrice, 2); $tmp['cost'] = $cost; $tmp['xhNum'] = $xhNum; $tmp['xhUnitName'] = $xhUnitName; $tmp['xhUnitType'] = $xhUnitType; $tmp['xhUnitPrice'] = $itemPrice; $tmp['xhPrice'] = $xhPrice; $tmp['xhPreNum'] = $xhNum; $tmp['xhPreUnitName'] = $xhUnitName; $tmp['xhPreUnitType'] = $xhUnitType; $tmp['xhPreUnitPrice'] = $itemPrice; $tmp['xhPrePrice'] = $xhPrice; $data[] = $tmp; $currentCost = bcmul($xhNum, $cost, 2); $totalCost = bcadd($totalCost, $currentCost, 2); // TODO 根据限购与特价进行价格计算的关键参数 } return ['product' => $data, 'totalReachDiscount' => $totalReachDiscount, 'smallNum' => $totalSmallNum, 'bigNum' => $totalBigNum, 'price' => $totalPrice, 'totalItemNum' => $totalItemNum, 'totalCost' => $totalCost]; } // //修改某个花材的成本价(采购之后要用到) lqh 2021.04.06 public static function changeCost($productId, $cost) { return self::updateById($productId, ['cost' => $cost]); } //更新某个花材的加价 2021.04.06 public static function changeAddPrice($productId, $addPrice) { return self::updateById($productId, ['addPrice' => $addPrice]); } //根据productId 计算花材的重量 public static function getWeight($productId, $bigNum, $smallNum) { $productInfo = self::getById($productId); if (empty($productInfo)) { return 0; } $ratio = $productInfo['ratio']; $weight = $productInfo['weight']; $mergeNum = self::mergeItemNum($bigNum, $smallNum, $ratio); return bcmul($mergeNum, $weight, 2); } /** * 修改product * @param $data * @param $shop * @param $type 所属平台:ghs 或 hd * @throws \Exception */ public static function updateProduct($data, $shop, $type) { $id = $data['id'] ?? 0; $adminId = $data['adminId'] ?? 0; $sjId = $shop->sjId ?? 0; $shopId = $shop->id ?? 0; $mainId = $shop->mainId ?? 0; $product = self::getLockById($id); if (empty($product)) { util::fail("该花材不存在"); } if (isset($data['limitBuy']) && intval($data['limitBuy']) > 0) { if ($product->limitBuy == 0) { $type == 'ghs' ? self::clearLimitBuyCache($id) : \bizHd\product\classes\ProductClass::clearLimitBuyCache($id); } } else { $type == 'ghs' ? self::clearLimitBuyCache($id) : \bizHd\product\classes\ProductClass::clearLimitBuyCache($id); } //扫码收款、直接收款,不包括特价商品,不可修改,多处要同步修改 system_item if (getenv('YII_ENV') == 'production') { $systemItem = [78076, 80960]; $adminList = [4, 1960]; } else { $systemItem = [11157, 11156]; $adminList = [919, 932]; } if (in_array($product->itemId, $systemItem)) { if (!in_array($adminId, $adminList)) { util::fail('系统花材,不可修改'); } } $name = $product->name ?? ''; $cover = $product->cover ?? ''; $upData = []; //库存可以修改 if (isset($data['stock']) && is_numeric($data['stock'])) { $preStock = $product->stock ?? 0; if ($data['stock'] != $preStock) { if ($data['stock'] < 0) { util::fail("库存不能小于0"); } if ($data['stock'] > 9999) { util::fail('库存数不能超过4个9'); } //增加盘点记录 2021.7.27 $ratio = $product->ratio ?? 0; $formNum = self::formatStock($data['stock'], $ratio); $ghsItemInfo = [['productId' => $product->id, 'bigNum' => $formNum['bigNum'], 'smallNum' => $formNum['smallNum'],]]; $pdData = []; $pdData['itemInfo'] = $ghsItemInfo; $pdData['sjId'] = $sjId; $pdData['shopId'] = $shopId; $pdData['adminId'] = $data['adminId']; $pdData['mainId'] = $mainId; $pdData['staffName'] = $data['staffName'] ?? ''; CheckOrderClass::opOrder($pdData, false, false); } unset($data['stock']); } unset($data['adminId']); if (isset($data['name'])) { if (empty($data['name'])) { util::fail("请输入花材名称"); } $upData['name'] = $data['name']; } if (!empty($data['py'])) { $upData['py'] = $data['py']; } if (!empty($data['cover'])) { $upData['cover'] = $data['cover']; } if (!empty($data['images'])) { $upData['images'] = $data['images']; } if (isset($data['discountPrice']) && is_numeric($data['discountPrice'])) { $discountPrice = $data['discountPrice'] ?? 0; $currentPrice = $data['price'] ?? 0; if ($discountPrice >= $currentPrice) { util::fail('特价要小于售价'); } $upData['discountPrice'] = $discountPrice; if ($discountPrice > 0) { if ($product->presell == 1) { util::fail('预售花材不能做特价'); } } $hjDiscountPrice = isset($data['hjDiscountPrice']) && is_numeric($data['hjDiscountPrice']) ? $data['hjDiscountPrice'] : 0; $skDiscountPrice = isset($data['skDiscountPrice']) && is_numeric($data['skDiscountPrice']) ? $data['skDiscountPrice'] : 0; if ($hjDiscountPrice > $discountPrice) { util::fail('批发特价要大于会员特价'); } if ($discountPrice > $skDiscountPrice) { util::fail('零售特价要大于批发特价'); } $upData['hjDiscountPrice'] = $hjDiscountPrice; $upData['skDiscountPrice'] = $skDiscountPrice; } $cost = $data['cost'] ?? 0; $price = $data['price'] ?? 0; $price = floatval($price); $skPrice = $data['skPrice'] ?? 0; $skPrice = floatval($skPrice); $hjPrice = $data['hjPrice'] ?? 0; $hjPrice = floatval($hjPrice); if (!is_numeric($price) || $price <= 0) { util::fail('请填写价格'); } if ($hjPrice > $price) { util::fail('批发价要大于会员价'); } if ($price > $skPrice) { util::fail('零售价要大于批发价'); } if (isset($data['addPrice']) && is_numeric($data['addPrice'])) { $upData['addPrice'] = $data['addPrice']; } if (isset($data['skMore']) && is_numeric($data['skMore'])) { $upData['skMore'] = $data['skMore']; } if (isset($data['hjAddPrice']) && is_numeric($data['hjAddPrice'])) { $upData['hjAddPrice'] = $data['hjAddPrice']; } if (floatval($price) != floatval($product->price) || floatval($skPrice) != floatval($product->skPrice) || floatval($hjPrice) != floatval($product->hjPrice)) { if ($shop->default == 0 && $shop->join == 0) { //除了小蔡鲜花的蔡总、郑总、苏南3007,其他人直营分店不能改价格,多处要同步修改,关键词 direct_price_change if (!in_array($adminId, [35725, 23659, 29264])) { util::fail('直营店不能改价哦'); } } } if (isset($data['limitBuy']) && is_numeric($data['limitBuy'])) { $upData['limitBuy'] = $data['limitBuy']; //取消限购 if ($data['limitBuy'] == 0) { if ($type == 'ghs') { self::clearLimitBuyCache($id); } else { \bizHd\product\classes\ProductClass::clearLimitBuyCache($id); } } } if (isset($data['stockWarning'])) { $upData['stockWarning'] = $data['stockWarning']; } //重量不可修改 unset($data['weight']); if (isset($data['inTurn'])) { $upData['inTurn'] = $data['inTurn']; } if (array_key_exists('itemRemark', $data)) { $itemRemark = trim($data['itemRemark'] ?? ''); if (mb_strlen($itemRemark) > 200) { util::fail('备注不能超过200字'); } $upData['itemRemark'] = $itemRemark; } if (isset($data['delStatus'])) { if (!in_array($data['delStatus'], [0, 1])) { util::fail("删除错误"); } $upData['delStatus'] = $data['delStatus']; if ($data['delStatus'] == 1) { $upData['discountPrice'] = 0; } } if (isset($data['status']) && is_numeric($data['status'])) { $upData['status'] = $data['status']; if ($data['status'] == 2) { $upData['discountPrice'] = 0; } } if (isset($data['frontHide']) && is_numeric($data['frontHide'])) { $upData['frontHide'] = $data['frontHide']; } if (isset($data['frontUse']) && is_numeric($data['frontUse'])) { $upData['frontUse'] = $data['frontUse']; } if (isset($data['variety']) && is_numeric($data['variety'])) { $upData['variety'] = $data['variety']; } //分类ID if (isset($data["classId"])) { $classId = $data['classId']; $upData['classId'] = $classId; if (isset($product->classId) && $product->classId != $classId) { //如果有修改分类,则在多个订单表中同步修改,如需修改,多个地方要同步修改,请搜索关键词item_class_modify_sync $itemClassKey = 'item_class_modify_sync'; Yii::$app->redis->executeCommand('LPUSH', [$itemClassKey, $id]); } } if (isset($data["scanNum"]) && is_numeric($data["scanNum"])) { $upData['scanNum'] = $data['scanNum']; } if (isset($data["reachNum"]) && $data["reachNum"] > 0) { $upData['reachNum'] = $data['reachNum']; if (!isset($data['reachNumDiscount']) || $data['reachNumDiscount'] <= 0) { util::fail('请填写满减的优惠金额'); } $reachNumDiscount = $data['reachNumDiscount']; $upData['reachNumDiscount'] = $reachNumDiscount; if ($reachNumDiscount >= $hjPrice) { util::fail('优惠金额不能大于售价'); } if (isset($upData['hjDiscountPrice']) && $upData['hjDiscountPrice'] > 0) { if ($reachNumDiscount >= $upData['hjDiscountPrice']) { util::fail('优惠金额不能大于特价'); } } if ($product->variety == 1) { util::fail('多颜色暂不支持满减'); } } else { $upData['reachNum'] = 0; $upData['reachNumDiscount'] = 0; } if (isset($data["smallRatio"]) && is_numeric($data["smallRatio"])) { $upData['smallRatio'] = $data['smallRatio']; } if (isset($data['ratioType'])) { $upData['ratioType'] = $data['ratioType']; } if (isset($data["smallShow"]) && is_numeric($data["smallShow"])) { $upData['smallShow'] = $data['smallShow']; } if (isset($data["cost"]) && is_numeric($data["cost"])) { $upData['cost'] = $data['cost']; } if (isset($data["virtualStock"]) && is_numeric($data["virtualStock"])) { $upData['virtualStock'] = $data['virtualStock']; } $paramsBack = $upData; $ptItemId = $product->itemId ?? 0; $staffId = $data['staffId'] ?? 0; $staffName = $data['staffName'] ?? ''; $changeParams = ['staffId' => $staffId, 'staffName' => $staffName]; if ($price != floatval($product->price) || $skPrice != floatval($product->skPrice) || $hjPrice != floatval($product->hjPrice)) { self::changeSinglePrice($shop, $ptItemId, $price, $skPrice, $hjPrice, $changeParams); unset($upData['price']); unset($upData['skPrice']); unset($upData['hjPrice']); if (!empty($mainId)) { //提醒零售收银台界面要刷新,多处要修改,关键词 modify_price_remind_cashier $staffList = ShopAdminClass::getAllByCondition(['mainId' => $mainId], null, '*', null, true); if (!empty($staffList)) { foreach ($staffList as $staff) { $staffId = $staff->id ?? 0; Yii::$app->redis->executeCommand('SET', ['cashier_' . $mainId . '_' . $staffId . '_may_refresh', 'yes']); Yii::$app->redis->executeCommand('SET', ['ghs_cashier_' . $mainId . '_' . $staffId . '_may_refresh', 'yes']); } } } } if ($cost != $product->cost) { $changeData = [ 'ptStyle' => 2, 'sjId' => $sjId, 'mainId' => $mainId, 'itemId' => $id, 'ptItemId' => $ptItemId, 'cost' => $cost, 'staffId' => $staffId, 'staffName' => $staffName, 'name' => $name ?? '', 'cover' => $cover, 'type' => 2, 'targetId' => $id ?? 0, 'event' => '修改', ]; CostChangeClass::addData($changeData); } self::updateById($id, $upData); //创建限购缓存 $limitBuy = $data['limitBuy'] ?? 0; if ($limitBuy > 0) { $limitBuyClearType = $data['limitBuyClearType']; $limitBuyClearLoopConfig = self::getLimitBuyClearLoopConfig($data); if ($limitBuyClearType == 1 && !empty($limitBuyClearLoopConfig)) { if ($type == 'ghs') { self::createRecurringLimitBuyCache($id, $limitBuyClearLoopConfig['intervalDays'], $limitBuyClearLoopConfig['clearHour']); } else { \bizHd\product\classes\ProductClass::createRecurringLimitBuyCache($id, $limitBuyClearLoopConfig['intervalDays'], $limitBuyClearLoopConfig['clearHour']); } } elseif ($limitBuyClearType == 2 && isset($data['limitBuyClearTime']) && $data['limitBuyClearTime'] != '') { $limitBuyClearTime = strtotime($data['limitBuyClearTime']); if ($limitBuyClearTime > time()) { if ($type == 'ghs') { self::createLimitBuyCache($id, $limitBuyClearTime - time()); } else { \bizHd\product\classes\ProductClass::createLimitBuyCache($id, $limitBuyClearTime - time()); } } else { util::fail('清空时间必须大于当前时间'); } } else { // 没有设置清空时间,则删除缓存 if ($type == 'ghs') { self::createLimitBuyCache($id, 0); } else { \bizHd\product\classes\ProductClass::createLimitBuyCache($id, 0); } } } //不影响其它直营店花材的上下架 unset($upData['status']); if (isset($shop->default) && $shop->default == 1 && isset($shop->dataSync) && $shop->dataSync == 1) { //总店修改同步到所有门店 $chainShopList = ShopClass::getAllByCondition(['sjId' => $sjId], null, '*', null, true); foreach ($chainShopList as $chainShop) { $chainShopId = $chainShop->id ?? 0; if ($chainShopId == $shop->id) { //前面已经添加过了 continue; } $chainMainId = $chainShop->mainId ?? 0; $params = $paramsBack; //所有门店上下架、启用停用等不同步 unset($params['status']); unset($params['delStatus']); unset($params['limitBuy']); $staff = ShopAdminClass::getByCondition(['mainId' => $chainMainId, 'adminId' => $adminId], true); $staffId = $staff->id ?? 0; $staffName = $staff->name ?? ''; $changeParams2 = ['staffId' => $staffId, 'staffName' => $staffName]; $chainProduct = self::getByCondition(['mainId' => $chainMainId, 'itemId' => $ptItemId], true); if (empty($chainProduct)) { continue; } //如果有同步分类则移动,没有则不移动 if (isset($params['classId'])) { $masterClassId = $params['classId']; unset($params['classId']); $masterClass = ItemClassClass::getById($masterClassId, true); $masterClassName = $masterClass->name ?? ''; $classList = ItemClassClass::getAllByCondition(['mainId' => $chainMainId], null, '*', null, true); if (!empty($classList)) { foreach ($classList as $currentClass) { if ($currentClass->name == $masterClassName) { $params['classId'] = $currentClass->id ?? 0; if (isset($chainProduct->classId) && $chainProduct->classId != $params['classId']) { //如果有修改分类,则在多个订单表中同步修改,如需修改,多个地方要同步修改,请搜索关键词item_class_modify_sync $itemClassKey = 'item_class_modify_sync'; Yii::$app->redis->executeCommand('LPUSH', [$itemClassKey, $chainProduct->id]); } } } } } //加盟店仅同步名称等非重要参数 if ($chainShop->join == 1) { unset($params['skPrice']); unset($params['price']); unset($params['hjPrice']); unset($params['addPrice']); unset($params['skMore']); unset($params['hjAddPrice']); unset($params['cost']); //特价的金额也不能同步,淘花里 unset($params['hjDiscountPrice']); unset($params['discountPrice']); unset($params['skDiscountPrice']); //满减也不能同步,惠雅鲜花批发 unset($params['reachNum']); unset($params['reachNumDiscount']); self::updateById($chainProduct->id, $params); continue; } if ($price != $chainProduct->price) { //批发价有变化 self::changeSinglePrice($chainShop, $ptItemId, $price, $skPrice, $hjPrice, $changeParams2); unset($params['price']); } if ($cost != $chainProduct->cost) { $changeData = [ 'ptStyle' => 2, 'sjId' => $chainShop->sjId ?? 0, 'mainId' => $chainShop->mainId ?? 0, 'itemId' => $chainProduct->id, 'ptItemId' => $ptItemId, 'cost' => $cost, 'staffId' => $staffId, 'staffName' => $staffName, 'name' => $chainProduct->name ?? '', 'cover' => $chainProduct->cover, 'type' => 2, 'targetId' => $chainProduct->id ?? 0, 'event' => '修改', ]; CostChangeClass::addData($changeData); } //直营店同步价格等重要因素 self::updateById($chainProduct->id, $params); } } } //变成自动改价 2021.4.14 public static function autoPriceById($productId) { $product = self::getById($productId, true); if (empty($product)) { return false; } $price = bcadd($product->cost, $product->addPrice, 2); self::changePrice($product, $price, self::PRICE_LABEL_AUTO); } //获取当前门店下所有的product public static function getAllProduct($sjId, $shopId, $field = "*") { $where = [ 'sjId' => $sjId, 'shopId' => $shopId, ]; return self::getAllByCondition($where, null, $field); } //获取当前门店下所有花材的库存数量,及成本价,库存预警的花材数量 lqh 2021-04-20 public static function getProductStockByShopId($sjId, $shopId) { $productInfo = self::getAllProduct($sjId, $shopId, "*"); $data = [ 'stockBigNum' => 0, 'stockSmallNum' => 0, 'stockCost' => 0, 'stockWarning' => 0, ]; if ($productInfo) { foreach ($productInfo as $v) { $stock = self::formatStock($v['stock'], $v['ratio']); $data['stockBigNum'] = bcadd($data['stockBigNum'], $stock['bigNum']); $data['stockSmallNum'] = bcadd($data['stockSmallNum'], $stock['smallNum']); $data['stockCost'] = bcadd($data['stockCost'], bcmul($v['stock'], $v['cost'], 2), 2); if ($v['stock'] <= $v['stockWarning']) { $data['stockWarning']++; } } } return $data; } //补className public static function groupClassName($mainId, $data) { $classNameMap = ItemClassClass::getAllByCondition(['mainId' => $mainId], null, 'id,name', 'id'); foreach ($data as $k => $v) { $classId = $v['classId']; $className = isset($classNameMap[$classId]) && isset($classNameMap[$classId]['name']) ? $classNameMap[$classId]['name'] : ''; $v['className'] = $className; $data[$k] = $v; } return $data; } //供货商的productId => 零售端的productId ,不存在的补productId(放在默认分类) //productList [{"productId":"113","bigNum":1,"smallNum":0,"classId":"-2","itemId":1}] public static function toggleProductList($productList, $hdShopId, $hdSjId, $hdMainId, $ghs) { $itemIds = array_column($productList, "itemId"); $hdProductData = self::getAllByCondition(['mainId' => $hdMainId, 'itemId' => ['in', $itemIds]], null, '*', 'itemId', true); $defaultClassId = ItemClassClass::getDefaultClassId($hdMainId); if (empty($defaultClassId)) { util::fail('没有找到花材的默认分类'); } foreach ($productList as $k => $v) { $itemId = $v['itemId'] ?? 0; $ghsProductId = $v['productId'] ?? 0; if (isset($hdProductData[$itemId]) == false) { //不存在,则从平台新增 $ptItem = PtItemClass::getById($itemId); unset($ptItem['addTime']); unset($ptItem['updateTime']); $productData = $ptItem; $productData['sjId'] = $hdSjId; $productData['shopId'] = $hdShopId; $productData['mainId'] = $hdMainId; $productData['itemId'] = $itemId; $productData['classId'] = $defaultClassId; unset($productData['id']); $productData['rank'] = 'A'; //计算价格 $cost = $v['cost'] ?? 0; $addPrice = $v['addPrice'] ?? 0; $price = bcadd($cost, $addPrice, 2); $userPrice = $v['userPrice'] ?? 0; if ($cost <= 0 && $price <= 0) { if ($userPrice > 0) { $price = $userPrice; $productData['cost'] = $price; } else { $price = 28; $productData['cost'] = 28; } $productData['skMore'] = 5; $productData['addPrice'] = 4; $productData['hjAddPrice'] = 3; } $productData['price'] = $price; $productData['skPrice'] = $price; $productData['hjPrice'] = $price; $productData['status'] = 2; //被归到默认分类,排序值要小,这样在花材列表搜索时,默认分类才不会被排在前面 $productData['inTurn'] = -1; $addProduct = self::addBaseData($productData); $hdProductId = $addProduct->id ?? 0; //通知新花材需要分类 //NotifyClass::cgGetNewProduct($addProduct, $hdMainId, $hdShopId, $ghs); } else { $current = $hdProductData[$itemId]; $hdProductId = $current->id ?? 0; $current->delStatus = 0; $current->save(); } $productList[$k]['productId'] = $hdProductId; $productList[$k]['ghsProductId'] = $ghsProductId; } return $productList; } //供货商、零售端新开门店时,初始化花材 public static function newShopInitProduct($sjId, $shopId, $newShopId, $adminId) { $product = self::getAllProduct($sjId, $shopId, "name,cover,classId,itemId,rank,price,priceLabel,cost,addPrice,stockWarning,weight,alias,py,ratio,bigUnit,smallUnit,bigUnitId,smallUnitId,inTurn"); $productData = []; foreach ($product as $v) { $tmp = $v; $tmp['sjId'] = $sjId; $tmp['shopId'] = $newShopId; $tmp['adminId'] = $adminId; $productData[] = $tmp; } self::batchAdd($productData); } //花材销量增加 public static function updateActualSoldById($id, $itemNum) { $data['actualSold'] = $itemNum; $res = self::updateById($id, $data); return $res; } //新增花材 public static function addProduct($data, $currentShop) { $currentMainId = $currentShop->mainId ?? 0; $classId = $data['classId'] ?? 0; $mainId = $data['mainId'] ?? 0; $stockWarning = $data['stockWarning'] ?? 5; $data['py'] = stringUtil::py($data['name']); $name = $data['name'] ?? ''; $data['alias'] = $data['alias'] ?? ''; $data['cover'] = !empty($data['cover']) ? $data['cover'] : ''; $ratioType = $data['ratioType'] ?? 0; if ($ratioType == 0) { if (!isset($data['ratio']) || $data['ratio'] <= 0) { util::fail('请填写几支装'); } $ratio = $data['ratio']; } else { $ratio = 10; $data['ratio'] = 10; } $weight = $data['weight'] ?? 0; $bigUnitId = $data['bigUnitId'] ?? 0; $smallUnitId = $data['smallUnitId'] ?? 0; if (empty($bigUnitId) || empty($smallUnitId)) { util::fail('请选择单位'); } $unitList = UnitClass::getByIds([$bigUnitId, $smallUnitId], null, 'id'); $bigUnit = $unitList[$bigUnitId]['name'] ?? '扎'; $smallUnit = $unitList[$smallUnitId]['name'] ?? '支'; $data['bigUnit'] = $bigUnit; $data['smallUnit'] = $smallUnit; $data['shopId'] = $data['shopId'] ?? 0; $currentShopId = $data['shopId']; $data['sjId'] = $data['sjId'] ?? 0; if (!$data['sjId']) { util::fail("参数错误"); } $stock = 0; if (isset($data['stock']) && is_numeric($data['stock'])) { if ($data['stock'] < 0) { util::fail("库存数不能小于0"); } if ($data['stock'] > 999999) { util::fail('库存数不能超过6个9'); } $stock = $data['stock']; } //没有库存则下架 $data['status'] = $stock == 0 ? 2 : 1; if (empty($classId)) { util::fail("请选择分类"); } if (!isset($data['price']) || !is_numeric($data['price']) || $data['price'] < 0) { util::fail("请输入价格"); } if (!isset($data['cost']) || !is_numeric($data['cost']) || $data['cost'] < 0) { util::fail("请输入成本价"); } if (!isset($data['addPrice']) || !is_numeric($data['addPrice'])) { util::fail("请输入加价"); } if ($stockWarning === false) { util::fail("请输入库存预警值"); } if ($name === false) { util::fail("请输入花材名称"); } if ($weight === false) { util::fail("请输入花材重量"); } if (isset($data['itemRemark'])) { $itemRemark = trim($data['itemRemark'] ?? ''); if (mb_strlen($itemRemark) > 200) { util::fail('备注不能超过200字'); } $data['itemRemark'] = $itemRemark; } $cat = ItemClassClass::getById($classId, true); if (empty($cat)) { util::fail("花材分类不存在"); } if ($cat->mainId != $mainId) { util::fail('不是您的分类'); } $catName = $cat->name ?? ''; if (isset($data['discountPrice']) && is_numeric($data['discountPrice'])) { $discountPrice = $data['discountPrice']; $currentPrice = $data['price'] ?? 0; if ($discountPrice >= $currentPrice) { util::fail('特价要小于售价'); } $hjDiscountPrice = isset($data['hjDiscountPrice']) && is_numeric($data['hjDiscountPrice']) ? $data['hjDiscountPrice'] : 0; $skDiscountPrice = isset($data['skDiscountPrice']) && is_numeric($data['skDiscountPrice']) ? $data['skDiscountPrice'] : 0; if ($hjDiscountPrice > $discountPrice) { util::fail('批发特价要大于会员特价'); } if ($discountPrice > $skDiscountPrice) { util::fail('零售特价要大于批发特价'); } } $connection = Yii::$app->db; $transaction = $connection->beginTransaction(); try { $ptItemId = $data['itemId'] ?? 0; $ptItemInfo = PtItemClass::getById($ptItemId, true); $bigUnitId = $data['bigUnitId'] ?? 0; $smallUnitId = $data['smallUnitId'] ?? 0; $changeUnit = 0; if ($ptItemInfo !== null && ($ptItemInfo->bigUnitId != $bigUnitId || $ptItemInfo->smallUnitId != $smallUnitId || $ptItemInfo->ratio != $ratio)) { //如果被修改了单位,则重新创建花材 $changeUnit = 1; } if (empty($ptItemId) || $changeUnit == 1) { //添加新品种先在平台新增花材 $defaultInfo = PtItemClassClass::getByCondition(['isDefault' => 1]); $defaultClassId = $defaultInfo['id'] ?? 0; if (empty($defaultClassId)) { util::fail('没有找到平台默认分类'); } $itemData = $data; $itemData['classId'] = $defaultClassId; $itemData['skAddPrice'] = $itemData['skAddPrice'] ?? 3; $itemData['hjAddPrice'] = $itemData['hjAddPrice'] ?? 2; $itemData['zsAddPrice'] = $itemData['zsAddPrice'] ?? 1; $ptItemInfo = PtItemClass::addItem($itemData); $data['itemId'] = $ptItemInfo['id']; } else { //从平台导入花材 $has = self::getByCondition(['mainId' => $mainId, 'itemId' => $data['itemId']], true); if (!empty($has)) { $hasName = $has->name ?? ''; util::fail($hasName . '已经存在了'); } } //小菊的结构 $data['variety'] = $ptItemInfo->variety ?? 0; //批发价比进货价贵多少 2021-12-07 lqh 如果前端有传取前端的,没有则取pt $addPrice = $data['addPrice'] ?? $ptItemInfo->addPrice; $data['addPrice'] = $addPrice; //批发价 $price = $data['price'] ?? 0; if (empty($price) || $price <= 0) { util::fail('请填写价格'); } $skPrice = $data['skPrice'] ?? 0; if (empty($skPrice) || $skPrice <= 0) { util::fail('请填写零售价'); } $hjPrice = $data['hjPrice'] ?? 0; if (empty($hjPrice) || $hjPrice <= 0) { util::fail('请填写会员价'); } if ($hjPrice > $price) { util::fail('批发价要大于会员价'); } if ($price > $skPrice) { util::fail('零售价要大于批发价'); } $data['property'] = 1; $respond = self::addBaseData($data); if ($stock > 0) { //有库存数量,增加盘盈记录 2021.7.9 lqh //增加盘点订单 $pdOrderSn = orderSn::getGhsCheckSn(); $pdOrderData = []; $pdOrderData['orderSn'] = $pdOrderSn; $pdOrderData['sjId'] = $data['sjId']; $pdOrderData['shopId'] = $data['shopId']; $pdOrderData['status'] = CheckOrderClass::STATUS_COMPLETE; $pdOrderData['bigNum'] = $stock; $pdOrderData['adminId'] = $data['adminId']; $pdOrderData['smallNum'] = 0; $pdOrderData['price'] = $data['price']; $pdOrderData['remark'] = '初始库存'; CheckOrderClass::add($pdOrderData); $pdOrderItemData = []; $pdOrderItemData['orderSn'] = $pdOrderSn; $pdOrderItemData['productId'] = $respond->id ?? 0; $pdOrderItemData['itemId'] = $data['itemId']; $pdOrderItemData['itemNum'] = $stock; $pdOrderItemData['itemStock'] = 0; $pdOrderItemData['profitLostNum'] = $stock; $itemInfo = [ 'itemName' => $name, 'itemCover' => $data['cover'], 'bigNum' => $stock, 'smallNum' => 0, 'bigNumStock' => 0, 'smallNumStock' => 0, 'bigNumProfitLoss' => $stock, 'smallNumProfitLoss' => 0, 'itemUnit' => 0, 'ratio' => $ratio, 'rank' => 'A', 'bigUnit' => $data['bigUnit'], 'smallUnit' => $data['smallUnit'], ]; $pdOrderItemData['itemInfo'] = json_encode($itemInfo); $pdOrderItemData['itemPrice'] = $data['price']; $pdOrderItemData['itemCost'] = $data['cost']; CheckOrderItemClass::add($pdOrderItemData); $record = []; $record['sjId'] = $data['sjId']; $record['shopId'] = $data['shopId']; //增加中央ID $record['mainId'] = $currentMainId; $record['orderSn'] = $pdOrderSn; $record['itemId'] = $data['itemId']; $record['productId'] = $respond->id ?? 0; $record['itemNum'] = $stock;//盈亏数 $record['oldStock'] = 0; $record['newStock'] = $stock; //新库存直接等于盘点的数量 $record['relateName'] = $data['staffName'] ?? ''; StockRecordClass::addCheckOrderRecord($record); } //在首店添加,则找到父级sj下的所有直营和加盟店,都进行同步 if (isset($currentShop->default) && $currentShop->default == 1) { $sjId = $currentShop->sjId ?? 0; $shopList = ShopClass::getAllByCondition(['sjId' => $sjId], null, '*', null, true); foreach ($shopList as $shop) { $shopId = $shop->id ?? 0; if ($shopId == $currentShop->id) { //前面已经添加过了 continue; } $thisMainId = $shop->mainId ?? 0; $data['shopId'] = $shopId; $data['mainId'] = $thisMainId; //不是当前门店,库存默认为0并且是下架状态。花材分类放在同名分类下,如果没有则放默认分类 if ($currentShopId != $shopId) { $data['stock'] = 0; $data['status'] = 2; $currentClassId = 0; $classList = ItemClassClass::getAllByCondition(['mainId' => $thisMainId], null, '*', null, true); if (!empty($classList)) { foreach ($classList as $currentClass) { $currentName = $currentClass->name ?? ''; if ($currentName == $catName) { $currentClassId = $currentClass->id ?? 0; } } } if (empty($currentClassId)) { $defaultCat = ItemClassClass::getByCondition(['mainId' => $thisMainId, 'isDefault' => 1], true); $currentClassId = $defaultCat->id ?? 0; } if (empty($currentClassId)) { util::fail('直营店没有分类,也没有默认分类'); } $data['classId'] = $currentClassId; } //已经有了不再添加 $chainHas = self::getByCondition(['mainId' => $thisMainId, 'itemId' => $ptItemId], true); if (!empty($chainHas)) { continue; } self::addBaseData($data); } } $transaction->commit(); } catch (\Exception $exception) { $transaction->rollBack(); Yii::info('失败原因:' . $exception->getMessage()); util::fail('添加失败'); } } /** * 改变花材状态并重置特价、满减、限购 * @param $product * @param $status 1上架 2下架 * @param bool $stockEmpty 库存为空(指新设置的库存) * @throws \Exception */ public static function changeStatusAndReset($product, $status, $stockEmpty = false) { $product->status = $status; // 当前库存为0 if (intval($product->stock) == 0) { //库存=0时下架,特价一起去掉 $product->discountPrice = 0; $product->hjDiscountPrice = 0; $product->skDiscountPrice = 0; //花材满减去掉 ssh 20260318 $product->reachNum = 0; $product->reachNumDiscount = 0; //花材去除限购 $product->limitBuy = 0; self::clearLimitBuyCache($product->id); // 清空限购记录缓存 } // 新设置的库存为0 if ($stockEmpty) { //库存=0时下架,特价一起去掉 $product->discountPrice = 0; $product->hjDiscountPrice = 0; $product->skDiscountPrice = 0; //花材满减去掉 ssh 20260318 $product->reachNum = 0; $product->reachNumDiscount = 0; //花材去除限购 $product->limitBuy = 0; // self::clearLimitBuyCache($productId); // 此处不清空限购记录缓存,留在更新库存的地方来做清空缓存 } } //获取当前供货商下的所有item public static function getGhsItemAll($sjId, $select = "*") { $where = [ 'sjId' => $sjId ]; $data = self::getAllList($select, $where); return $data; } //库存预警通知 ssh 20210909 public static function stockWarningAddQueue($product, $stock) { $warningNum = $product['stockWarning'] ?? 0; if ($warningNum <= 0) { return false; } $date = date("Y-m-d"); $productId = $product['id'] ?? 0; $key = 'stockWarning_' . $productId . '_' . $date; //当天已经放入队列通知的不再通知 $hasWarning = Yii::$app->redis->executeCommand('GET', [$key]); if (isset($hasWarning) && !empty($hasWarning)) { return false; } if ($stock <= $warningNum) { Yii::$app->redis->executeCommand('SETEX', [$key, 86400, 'hasWarning']); $shopId = $product['shopId'] ?? 0; if (empty($shopId)) { return false; } //队列方式去处理 $stockWarningListKey = 'stockWarningList_' . $shopId; Yii::$app->redis->executeCommand('LPUSH', [$stockWarningListKey, $productId]); } } //所有门店进行库存通知 ssh 20210909 public static function allShopStockWarningFromQueue() { //先取消库存预警 return true; $ghs = dict::getDict('ptStyle', 'ghs'); $shopList = ShopClass::getAllByCondition(['ptStyle' => $ghs], null, '*', null, true); if (!empty($shopList)) { foreach ($shopList as $key => $shop) { $ids = []; $shopId = $shop->id ?? 0; $stockWarningListKey = 'stockWarningList_' . $shopId; while ($count = Yii::$app->redis->executeCommand('LLEN', [$stockWarningListKey])) { $productId = Yii::$app->redis->executeCommand('RPOP', [$stockWarningListKey]); if (is_numeric($productId)) { $ids[] = $productId; } } if (empty($ids)) { continue; } $ids = array_unique(array_filter($ids)); $productList = self::getAllByCondition(['id' => ['in', $ids]], null, '*', null, true); if (!empty($productList)) { WxMessageClass::stockWarningInform($shop, $productList); } } } } //通过花材ID 获取mainId lqh 2022.03.20 public static function getMainIdByProductId($productId) { $product = self::getById($productId, false); return $product['mainId'] ?? 0; } /** * 创建限购缓存(有过期时间) * @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' => 'ghs', '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' => 'ghs', '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 getLimitBuyClearLoopConfig($data) { if ($data['limitBuyClearType'] == 0) { return []; } $hasIntervalDays = array_key_exists('limitBuyClearIntervalDays', $data) && $data['limitBuyClearIntervalDays'] !== ''; $hasClearHour = (array_key_exists('cleartAt', $data) && $data['cleartAt'] !== '') || (array_key_exists('clearAt', $data) && $data['clearAt'] !== ''); if (!$hasIntervalDays && !$hasClearHour) { return []; } if (!$hasIntervalDays || !$hasClearHour) { util::fail('请填写循环清空限购的间隔天数和执行时间'); } if (!is_numeric($data['limitBuyClearIntervalDays'])) { util::fail('循环清空限购的间隔天数错误'); } $clearHourValue = array_key_exists('cleartAt', $data) ? $data['cleartAt'] : $data['clearAt']; if (!is_numeric($clearHourValue)) { util::fail('循环清空限购的执行时间错误'); } $intervalDays = intval($data['limitBuyClearIntervalDays']); $clearHour = intval($clearHourValue); if ($intervalDays <= 0) { util::fail('循环清空限购的间隔天数必须大于0'); } if ($clearHour < 0 || $clearHour > 23) { util::fail('循环清空限购的执行时间必须在0点到23点之间'); } return ['intervalDays' => $intervalDays, 'clearHour' => $clearHour]; } /** * 创建循环清空限购缓存 * @param $productId 花材ID * @param int $intervalDays 循环清空限购的间隔天数 * @param int $clearHour 循环清空限购的执行时间点 * @param int $clearAt 循环清空限购的下次清空时间 * @return bool */ 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 * @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), ]; } /** * 清理当前请求中记录的限购回滚快照 * * @return void */ public static function clearLimitBuyRollbackSnapshot() { unset(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY]); } /** * 回滚当前请求中已记录的限购缓存变更 * * @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; } // 获取客户当前采购清单中各花材的限购情况 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 = self::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 = self::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, //超出数量, 小于等于0表示未超出 ]; } return $respond; } /** * 限购处理 * @param $product * @param $customId * @param $num */ public static function handleLimitBuy($product, $customId, $num) { $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); 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]); } 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]); } Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $num]); } } /** * 清空限购的缓存 * @param int $productId * @param bool $clearAll */ 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 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; } //$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 getHasLimitBuyList($product) { $productId = $product->id; $limitBuy = $product->limitBuy; $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 int $productId * @return bool */ public static function clearLimitBuyByProductId($productId, $oldLimitBuy = 0) { $productId = intval($productId); if ($productId <= 0) { return false; } self::updateById($productId, ['limitBuy' => 0]); if ($oldLimitBuy > 0) { self::createRecoverLimitBuyCache($productId, $oldLimitBuy); } return true; } /** * 创建限购旧值快照缓存 * @param $productId * @param $oldLimitBuy * @return bool */ public static function createRecoverLimitBuyCache($productId, $oldLimitBuy) { $productId = intval($productId); if ($productId <= 0) { return false; } $limitKey = self::RECOVER_LIMIT_BUY_KEY . $productId; Yii::$app->redis->executeCommand('SET', [$limitKey, $oldLimitBuy]); //Yii::$app->redis->executeCommand('EXPIRE', [$limitKey, 1200]);// 要考虑售后退款,所以不能主动清空,而是只能被动清空 return true; } /** * 删除限购旧值快照缓存 * @param $productId * @return bool */ public static function deleteRecoverLimitBuyCache($productId) { $productId = intval($productId); if ($productId <= 0) { return false; } $limitKey = self::RECOVER_LIMIT_BUY_KEY . $productId; Yii::$app->redis->executeCommand('DEL', [$limitKey]); return true; } /** * 获取限购旧值快照缓存 * @param $productId * @return bool */ public static function getRecoverLimitBuyCache($productId) { $productId = intval($productId); if ($productId <= 0) { return false; } $limitKey = self::RECOVER_LIMIT_BUY_KEY . $productId; return Yii::$app->redis->executeCommand('GET', [$limitKey]); } /** * 库存从 0 回滚时,尝试恢复限购数量 * @param int $productId * @param mixed $oldStock * @return bool */ public static function tryRecoverLimitBuyWhenStockRollbackFromZero($productId, $oldStock) { if (floatval($oldStock) > 0) { return false; } $recoverLimitBuy = self::getRecoverLimitBuyCache($productId); if ($recoverLimitBuy === false || $recoverLimitBuy === null || $recoverLimitBuy === '') { return false; } $recoverLimitBuy = intval($recoverLimitBuy); if ($recoverLimitBuy <= 0) { self::deleteRecoverLimitBuyCache($productId); return false; } self::updateById($productId, ['limitBuy' => $recoverLimitBuy]); self::deleteRecoverLimitBuyCache($productId); return true; } }