|
|
@@ -8,10 +8,14 @@ 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 = 'hd_limit_buy_clear_at:';
|
|
|
+ const LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY = 'hd_limitBuyRollbackSnapshot'; //记录限购旧值快照缓存键
|
|
|
|
|
|
public static $baseFile = '\bizHd\product\models\Product';
|
|
|
|
|
|
@@ -424,10 +428,53 @@ class ProductClass extends BaseClass
|
|
|
return $list;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 限购处理
|
|
|
+ * @param $product
|
|
|
+ * @param $customId 零售xhCustom表的id
|
|
|
+ * @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]);
|
|
|
+ $lastNum = bcadd($hasNum, $num);
|
|
|
+ if ($lastNum > $limitBuy) {
|
|
|
+ // 如果有特价,则超过的数量按原价卖;否则中断
|
|
|
+ if ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ util::error(-1, '累计已超出限购数', ['productId'=>$productId, 'limitBuy'=>$limitBuy, 'exceed'=>$lastNum - $limitBuy]);
|
|
|
+ }
|
|
|
+ self::recordLimitBuyRollbackSnapshot($productId, $customId, true, $hasNum);
|
|
|
+ Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $lastNum]);
|
|
|
+ } else {
|
|
|
+ if ($num > $limitBuy) {
|
|
|
+ // 如果有特价,则超过的数量按原价卖;否则中断
|
|
|
+ if ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ util::error(-1, '超出限购数', ['productId'=>$productId, 'limitBuy'=>$limitBuy, 'exceed'=>$num - $limitBuy]);
|
|
|
+ }
|
|
|
+ self::recordLimitBuyRollbackSnapshot($productId, $customId, false, 0);
|
|
|
+ Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $num]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
//取消限购的缓存
|
|
|
public static function cancelLimitBuy($productId)
|
|
|
{
|
|
|
- $limitKey = 'limit_buy_' . $productId;
|
|
|
+ $limitKey = self::LIMIT_BUY_KEY . $productId;
|
|
|
$arr = Yii::$app->redis->executeCommand('HKEYS', [$limitKey]);
|
|
|
if (!empty($arr)) {
|
|
|
foreach ($arr as $field) {
|
|
|
@@ -437,10 +484,14 @@ class ProductClass extends BaseClass
|
|
|
self::clearLimitBuyClearMark($productId);
|
|
|
}
|
|
|
|
|
|
- //$num -1 表示减全部,大于-1表示减值
|
|
|
+ /**
|
|
|
+ * @param $productId
|
|
|
+ * @param $customId
|
|
|
+ * @param int $num -1 表示减全部,大于-1表示减值
|
|
|
+ */
|
|
|
public static function baseClearLimitBuy($productId, $customId, $num = -1)
|
|
|
{
|
|
|
- $limitKey = 'limit_buy_' . $productId;
|
|
|
+ $limitKey = self::LIMIT_BUY_KEY . $productId;
|
|
|
if ($num <= -1) {
|
|
|
//清掉全部
|
|
|
Yii::$app->redis->executeCommand('HDEL', [$limitKey, $customId]);
|
|
|
@@ -463,9 +514,106 @@ class ProductClass extends BaseClass
|
|
|
if ($productId <= 0) {
|
|
|
return false;
|
|
|
}
|
|
|
- $clearMarkKey = 'limit_buy_clear_at:' . $productId;
|
|
|
+ $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
|
|
|
Yii::$app->redis->executeCommand('DEL', [$clearMarkKey]);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ // 创建限购缓存(有过期时间)
|
|
|
+ public static function createLimitBuyCache($productId, $seconds = 0)
|
|
|
+ {
|
|
|
+ $limitKey = self::LIMIT_BUY_KEY . $productId;
|
|
|
+ $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
|
|
|
+
|
|
|
+ $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 = time() + intval($seconds);
|
|
|
+ Yii::$app->redis->executeCommand('EXPIRE', [$limitKey, $seconds]);
|
|
|
+ Yii::$app->redis->executeCommand('SET', [$clearMarkKey, $clearAt]);
|
|
|
+
|
|
|
+ // 用 RabbitMQ 延迟消息在到期时清空订单项限购字段
|
|
|
+ $message = [
|
|
|
+ 'type' => 'limit_buy_clear',
|
|
|
+ 'productId' => $productId,
|
|
|
+ 'clearAt' => $clearAt,
|
|
|
+ ];
|
|
|
+ $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),
|
|
|
+ ], JSON_UNESCAPED_UNICODE), __METHOD__);
|
|
|
+ } else {
|
|
|
+ Yii::$app->redis->executeCommand('DEL', [$clearMarkKey]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录限购缓存写入前快照(同一次请求内同商品同客户只记录一次)
|
|
|
+ *
|
|
|
+ * @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;
|
|
|
+ }
|
|
|
+
|
|
|
}
|