|
|
@@ -48,6 +48,7 @@ class ProductClass extends BaseClass
|
|
|
|
|
|
const LIMIT_BUY_KEY = 'limit_buy_';
|
|
|
const CLEAR_MARK_KEY = 'limit_buy_clear_at:';
|
|
|
+ const CLEAR_LOOP_KEY = 'limit_buy_clear_loop:';
|
|
|
const LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY = 'limitBuyRollbackSnapshot'; //记录限购旧值快照缓存键
|
|
|
|
|
|
public static function getNotShowFrontHideMainIds()
|
|
|
@@ -1957,16 +1958,26 @@ class ProductClass extends BaseClass
|
|
|
self::updateById($id, $upData);
|
|
|
|
|
|
//创建限购缓存
|
|
|
- if ($data['limitBuy'] > 0 && isset($data['limitBuyClearTime']) && $data['limitBuyClearTime'] != '') {
|
|
|
- $limitBuyClearTime = strtotime($data['limitBuyClearTime']);
|
|
|
- if ($limitBuyClearTime > time()) {
|
|
|
+ $limitBuy = $data['limitBuy'] ?? 0;
|
|
|
+ if ($limitBuy > 0) {
|
|
|
+ $limitBuyClearLoopConfig = self::getLimitBuyClearLoopConfig($data);
|
|
|
+ if (!empty($limitBuyClearLoopConfig)) {
|
|
|
if ($type == 'ghs') {
|
|
|
- self::createLimitBuyCache($id, $limitBuyClearTime - time());
|
|
|
+ self::createRecurringLimitBuyCache($id, $limitBuyClearLoopConfig['intervalDays'], $limitBuyClearLoopConfig['clearHour']);
|
|
|
} else {
|
|
|
- \bizHd\product\classes\ProductClass::createLimitBuyCache($id, $limitBuyClearTime - time());
|
|
|
+ \bizHd\product\classes\ProductClass::createRecurringLimitBuyCache($id, $limitBuyClearLoopConfig['intervalDays'], $limitBuyClearLoopConfig['clearHour']);
|
|
|
+ }
|
|
|
+ } elseif (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 {
|
|
|
- util::fail('清空时间必须大于当前时间');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2691,11 +2702,19 @@ class ProductClass extends BaseClass
|
|
|
return $product['mainId'] ?? 0;
|
|
|
}
|
|
|
|
|
|
- // 创建限购缓存(有过期时间)
|
|
|
- public static function createLimitBuyCache($productId, $seconds = 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)) {
|
|
|
@@ -2707,12 +2726,27 @@ class ProductClass extends BaseClass
|
|
|
//删除缓存
|
|
|
Yii::$app->redis->executeCommand('HDEL', [$limitKey, 0]);
|
|
|
}
|
|
|
+
|
|
|
Yii::$app->redis->executeCommand('HSET', [$limitKey, 0, 0]);
|
|
|
if ($seconds > 0) {
|
|
|
- $clearAt = time() + intval($seconds);
|
|
|
+ $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',
|
|
|
@@ -2720,6 +2754,11 @@ class ProductClass extends BaseClass
|
|
|
'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', [
|
|
|
@@ -2735,10 +2774,104 @@ class ProductClass extends BaseClass
|
|
|
'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)
|
|
|
+ {
|
|
|
+ $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];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建循环清空限购缓存
|
|
|
+ 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 clearLimitBuyClearLoopConfig($productId)
|
|
|
+ {
|
|
|
+ $productId = intval($productId);
|
|
|
+ if ($productId <= 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $clearLoopKey = self::CLEAR_LOOP_KEY . $productId;
|
|
|
+ Yii::$app->redis->executeCommand('DEL', [$clearLoopKey]);
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -2868,6 +3001,7 @@ class ProductClass extends BaseClass
|
|
|
}
|
|
|
}
|
|
|
self::clearLimitBuyClearMark($productId);
|
|
|
+ self::clearLimitBuyClearLoopConfig($productId);
|
|
|
}
|
|
|
|
|
|
// 校验清空限购消息是否是当前有效版本
|
|
|
@@ -2915,6 +3049,18 @@ class ProductClass extends BaseClass
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 只清空已购买记录,不关闭商品限购配置
|
|
|
+ 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;
|