ActivityRedis.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. <?php
  2. namespace addons\shopro\library\activity\traits;
  3. use addons\shopro\facade\Redis;
  4. use app\admin\model\shopro\activity\Activity;
  5. use think\helper\Str;
  6. /**
  7. * 获取活动 redis 基础方法
  8. */
  9. trait ActivityRedis
  10. {
  11. protected $zsetKey = 'zset-activity'; // 活动集合 key
  12. protected $hashPrefix = 'hash-activity:'; // 活动前缀
  13. protected $hashGoodsPrefix = 'goods-'; // 活动中商品的前缀
  14. protected $hashGrouponPrefix = 'groupon-';
  15. // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 获取活动相关信息 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  16. /**
  17. * 获取活动完整信息
  18. *
  19. * @param integer $id
  20. * @param string $type
  21. * @return array
  22. */
  23. public function getActivity($id, $type)
  24. {
  25. $keyActivity = $this->keyActivity($id, $type);
  26. return $this->getActivityByKey($keyActivity);
  27. }
  28. /**
  29. * 通过活动的键值,获取活动完整信息
  30. *
  31. * @param string $activityHashKey
  32. * @return array
  33. */
  34. public function getActivityByKey($keyActivity)
  35. {
  36. // 取出整条 hash 记录
  37. $activity = Redis::HGETALL($keyActivity);
  38. return $activity;
  39. }
  40. /**
  41. * 删除活动
  42. *
  43. * @param integer $id
  44. * @param string $type
  45. * @return void
  46. */
  47. public function delActivity($id, $type)
  48. {
  49. $keyActivity = $this->keyActivity($id, $type);
  50. $this->delActivityByKey($keyActivity);
  51. }
  52. /**
  53. * 通过 key 删除活动
  54. *
  55. * @param string $keyActivity
  56. * @return void
  57. */
  58. public function delActivityByKey($keyActivity)
  59. {
  60. // 删除 hash
  61. Redis::DEL($keyActivity);
  62. // 删除集合
  63. Redis::ZREM($this->zsetKey, $keyActivity);
  64. }
  65. /**
  66. * 获取活动的状态
  67. *
  68. * @param string $keyActivity
  69. * @return string
  70. */
  71. public function getActivityStatusByKey($keyActivity)
  72. {
  73. $prehead_time = Redis::HGET($keyActivity, 'prehead_time'); // 预热时间
  74. $start_time = Redis::HGET($keyActivity, 'start_time'); // 开始时间
  75. $end_time = Redis::HGET($keyActivity, 'end_time'); // 结束时间
  76. // 获取活动状态
  77. $status = Activity::getStatusCode($prehead_time, $start_time, $end_time);
  78. return $status;
  79. }
  80. // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 获取活动相关信息 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  81. // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 操作活动 hash ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  82. /**
  83. * 计算每个规格的真实库存、销量
  84. *
  85. * @param array $goods
  86. * @param string $keyActivity
  87. * @return array
  88. */
  89. private function calcGoods($goods, $keyActivity)
  90. {
  91. // 销量 key
  92. $keyActivityGoods = $this->keyActivityGoods($goods['goods_id'], $goods['goods_sku_price_id'], true);
  93. // 缓存中的销量
  94. $cacheSale = Redis::HGET($keyActivity, $keyActivityGoods);
  95. $stock = $goods['stock'] - $cacheSale;
  96. $goods['stock'] = $stock > 0 ? $stock : 0;
  97. $goods['sales'] = $cacheSale;
  98. return $goods;
  99. }
  100. // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 操作活动 hash ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  101. // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 格式化活动内容 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  102. /**
  103. * 格式化活动
  104. *
  105. * @param string array $keyActivity 活动 key 或者活动完整信息
  106. * @param string $type 格式化方式
  107. * @param array $data 额外参数
  108. * @return array
  109. */
  110. public function formatActivityByKey($keyActivity, $type = 'normal', $data = [])
  111. {
  112. $activity = $this->{'formatActivity' . Str::studly($type)}($keyActivity, $data);
  113. return $activity;
  114. }
  115. /**
  116. * 正常模式,只移除销量, 团信息,保留全部商品规格数据
  117. *
  118. * @param string|array $originalActivity
  119. * @param array $data 额外数据,商品 id
  120. * @return array|null
  121. */
  122. public function formatActivityNormal($originalActivity, $data = [])
  123. {
  124. if (is_string($originalActivity)) {
  125. // 传入的是活动的key
  126. $keyActivity = $originalActivity;
  127. $originalActivity = $this->getActivityByKey($originalActivity);
  128. } else {
  129. $keyActivity = $this->keyActivity($originalActivity['id'], $originalActivity['type']);
  130. }
  131. $activity = [];
  132. foreach ($originalActivity as $key => $value) {
  133. // 包含 -sale 全部跳过
  134. if (strpos($key, '-sale') !== false) {
  135. continue;
  136. } else if (strpos($key, $this->hashGrouponPrefix) !== false) {
  137. // 拼团的参团人数,团用户,移除
  138. continue;
  139. } else if ($key == 'rules') {
  140. $activity[$key] = json_decode($value, true);
  141. } else {
  142. // 普通键值
  143. $activity[$key] = $value;
  144. }
  145. }
  146. if ($activity) {
  147. // 处理活动状态
  148. $activity['status'] = Activity::getStatusCode($activity['prehead_time'], $activity['start_time'], $activity['end_time']);
  149. $activity['status_text'] = Activity::getStatusText($activity['status']);
  150. }
  151. return $activity ?: null;
  152. }
  153. /**
  154. * 简洁模式,只保留活动表基本信息
  155. *
  156. * @param string $originalActivity
  157. * @param array $data 额外数据,商品 id
  158. * @return array|null
  159. */
  160. private function formatActivityClear($originalActivity, $data = [])
  161. {
  162. if (is_string($originalActivity)) {
  163. // 传入的是活动的key
  164. $keyActivity = $originalActivity;
  165. $originalActivity = $this->getActivityByKey($originalActivity);
  166. } else {
  167. $keyActivity = $this->keyActivity($originalActivity['id'], $originalActivity['type']);
  168. }
  169. $activity = [];
  170. foreach ($originalActivity as $key => $value) {
  171. // 包含 -sale 全部跳过
  172. if (strpos($key, $this->hashGoodsPrefix) !== false) {
  173. continue;
  174. } else if (strpos($key, $this->hashGrouponPrefix) !== false) {
  175. // 拼团的参团人数,团用户,移除
  176. continue;
  177. } else if ($key == 'rules') {
  178. $activity[$key] = json_decode($value, true);
  179. } else {
  180. // 普通键值
  181. $activity[$key] = $value;
  182. }
  183. }
  184. if ($activity) {
  185. // 处理活动状态
  186. $activity['status'] = Activity::getStatusCode($activity['prehead_time'], $activity['start_time'], $activity['end_time']);
  187. $activity['status_text'] = Activity::getStatusText($activity['status']);
  188. }
  189. return $activity ?: null;
  190. }
  191. /**
  192. * 获取并按照商品展示格式化活动数据
  193. *
  194. * @param string $originalActivity hash key
  195. * @param array $data 额外数据,商品 id
  196. * @return array|null
  197. */
  198. private function formatActivityGoods($originalActivity, $data = [])
  199. {
  200. $goods_id = $data['goods_id'] ?? 0;
  201. if (is_string($originalActivity)) {
  202. // 传入的是活动的key
  203. $keyActivity = $originalActivity;
  204. $originalActivity = $this->getActivityByKey($originalActivity);
  205. } else {
  206. $keyActivity = $this->keyActivity($originalActivity['id'], $originalActivity['type']);
  207. }
  208. $activity = [];
  209. // 商品前缀
  210. $goodsPrefix = $this->hashGoodsPrefix . ($goods_id ? $goods_id . '-' : '');
  211. foreach ($originalActivity as $key => $value) {
  212. // 包含 -sale 全部跳过
  213. if (strpos($key, '-sale') !== false) {
  214. continue;
  215. } else if (strpos($key, $goodsPrefix) !== false) {
  216. // 商品规格信息,或者特定商品规格信息
  217. $goods = json_decode($value, true);
  218. // 计算销量库存数据
  219. $goods = $this->calcGoods($goods, $keyActivity);
  220. // 商品规格项
  221. $activity['activity_sku_prices'][] = $goods;
  222. } else if ($goods_id && strpos($key, $this->hashGoodsPrefix) !== false) {
  223. // 需要特定商品时,移除别的非当前商品的数据
  224. continue;
  225. } else if (strpos($key, $this->hashGrouponPrefix) !== false) {
  226. // 拼团的参团人数,团用户,移除
  227. continue;
  228. } else if ($key == 'rules') {
  229. $activity[$key] = json_decode($value, true);
  230. } else {
  231. // 普通键值
  232. $activity[$key] = $value;
  233. }
  234. }
  235. if ($activity) {
  236. // 处理活动状态
  237. $activity['status'] = Activity::getStatusCode($activity['prehead_time'], $activity['start_time'], $activity['end_time']);
  238. $activity['status_text'] = Activity::getStatusText($activity['status']);
  239. }
  240. return $activity ?: null;
  241. }
  242. /**
  243. * 获取并按照折扣格式展示格式化活动数据
  244. *
  245. * @param string $originalActivity hash key
  246. * @param array $data 额外数据
  247. * @return array|null
  248. */
  249. public function formatActivityPromo($originalActivity, $data = [])
  250. {
  251. if (is_string($originalActivity)) {
  252. // 传入的是活动的key
  253. $keyActivity = $originalActivity;
  254. $originalActivity = $this->getActivityByKey($originalActivity);
  255. } else {
  256. $keyActivity = $this->keyActivity($originalActivity['id'], $originalActivity['type']);
  257. }
  258. $activity = [];
  259. foreach ($originalActivity as $key => $value) {
  260. if ($key == 'rules') {
  261. $rules = json_decode($value, true);
  262. $activity[$key] = $rules;
  263. } else {
  264. // 普通键值
  265. $activity[$key] = $value;
  266. }
  267. }
  268. if ($activity) {
  269. // 处理活动状态
  270. $activity['status'] = Activity::getStatusCode($activity['prehead_time'], $activity['start_time'], $activity['end_time']);
  271. $activity['status_text'] = Activity::getStatusText($activity['status']);
  272. }
  273. return $activity ?: null;
  274. }
  275. // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 格式化活动内容 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  276. // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 获取活动的 keys 数组 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  277. /**
  278. * 获取所有活动的 keys
  279. *
  280. * @return array
  281. */
  282. public function getKeysActivity()
  283. {
  284. // 获取活动集合
  285. $keysActivity = Redis::ZRANGE($this->zsetKey, 0, 999999999);
  286. return $keysActivity;
  287. }
  288. /**
  289. * 通过活动 id 获取活动的 key(不知道活动类型,只知道 id 的时候用)
  290. *
  291. * @param integer $id
  292. * @return string
  293. */
  294. public function getKeyActivityById($id)
  295. {
  296. $keysActivity = $this->getKeysActivity();
  297. foreach ($keysActivity as $keyActivity) {
  298. $suffix = ':' . $id;
  299. // 判断是否是要找的活动id, 截取 hashKey 后面几位,是否为当前要查找的活动 id
  300. if (substr($keyActivity, (strlen($keyActivity) - strlen($suffix))) == $suffix) {
  301. $currentKeyActivity = $keyActivity;
  302. break;
  303. }
  304. }
  305. return $currentKeyActivity ?? null;
  306. }
  307. /**
  308. * 通过活动 id 和 活动 type 获取 活动 key
  309. *
  310. * @param integer $activity_id
  311. * @param string $activity_type
  312. * @return string
  313. */
  314. public function getKeyActivityByIdType($activity_id, $activity_type)
  315. {
  316. $keyActivity = $this->keyActivity($activity_id, $activity_type);
  317. return $keyActivity;
  318. }
  319. /**
  320. * 获取对应活动类型的 活动 keys
  321. *
  322. * @param array $activityTypes
  323. * @param array|string $status 要查询的活动的状态
  324. * @return array
  325. */
  326. public function getKeysActivityByTypes($activityTypes, $status = 'all')
  327. {
  328. $status = is_array($status) ? $status : [$status];
  329. $activityTypes = is_array($activityTypes) ? $activityTypes : [$activityTypes];
  330. $activityTypes = array_values(array_filter(array_unique($activityTypes))); // 过滤空值
  331. $keysActivity = $this->getKeysActivity();
  332. // 获取对应的活动类型的集合
  333. $keysActivityTypes = [];
  334. foreach ($keysActivity as $keyActivity) {
  335. // 循环要查找的活动类型数组
  336. foreach ($activityTypes as $type) {
  337. $prefix = $this->hashPrefix . $type . ':';
  338. if (strpos($keyActivity, $prefix) === 0) { // 是要查找的类型
  339. $keysActivityTypes[] = $keyActivity;
  340. break;
  341. }
  342. }
  343. }
  344. // 判断活动状态
  345. if (!in_array('all', $status)) {
  346. foreach ($keysActivityTypes as $key => $keyActivity) {
  347. $activity_status = $this->getActivityStatusByKey($keyActivity);
  348. if (!in_array($activity_status, $status)) {
  349. unset($keysActivityTypes[$key]);
  350. }
  351. }
  352. }
  353. return array_values($keysActivityTypes);
  354. }
  355. /**
  356. * 通过商品获取该商品参与的活动的hash key
  357. *
  358. * @param integer $goods_id
  359. * @param Array $activityType
  360. * @param array|string $status 要查询的活动的状态
  361. * @return array
  362. */
  363. private function getkeysActivityByGoods($goods_id, $activityType = [], $status = 'all')
  364. {
  365. // 获取对应类型的活动集合
  366. $keysActivity = $this->getKeysActivityByTypes($activityType, $status);
  367. $keysActivityGoods = [];
  368. foreach ($keysActivity as $keyActivity) {
  369. // 判断这条活动是否包含该商品
  370. $goods_ids = array_filter(explode(',', Redis::HGET($keyActivity, 'goods_ids')));
  371. if (!in_array($goods_id, $goods_ids) && !empty($goods_ids)) {
  372. continue;
  373. }
  374. $keysActivityGoods[] = $keyActivity;
  375. }
  376. return $keysActivityGoods;
  377. }
  378. // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 获取活动的 keys 数组 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  379. // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 获取活动相关 key ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  380. /**
  381. * 获取活动 hash 的 key
  382. *
  383. * @param integer $activity_id 活动 id
  384. * @param string $activity_type 活动类型
  385. * @return string
  386. */
  387. private function keyActivity($activity_id, $activity_type)
  388. {
  389. // 示例 hash-activity:groupon:25
  390. return $this->hashPrefix . $activity_type . ':' . $activity_id;
  391. }
  392. /**
  393. * 获取活动 hash 中 商品相关的 key (is_sale 对应的活动商品的销量)
  394. *
  395. * @param integer $goods_id 商品
  396. * @param integer $sku_price_id 规格
  397. * @param boolean $is_sale 对应的活动商品的销量
  398. * @return string
  399. */
  400. private function keyActivityGoods($goods_id, $sku_price_id, $is_sale = false)
  401. {
  402. // 示例 商品规格:goods-25-30 or 商品规格销量:goods-25-30-sale
  403. return $this->hashGoodsPrefix . $goods_id . '-' . $sku_price_id . ($is_sale ? '-sale' : '');
  404. }
  405. /**
  406. * 获取活动中 拼团 的团数据的 key
  407. *
  408. * @param integer $groupon_id
  409. * @param integer $goods_id
  410. * @param string $type 空=团 key|num=团人数|users=团用户
  411. * @return string
  412. */
  413. private function keyActivityGroupon($groupon_id, $goods_id, $type = '')
  414. {
  415. return $this->hashGrouponPrefix . $groupon_id . '-' . $goods_id . ($type ? '-' . $type : '');
  416. }
  417. /**
  418. * 获取活动相关的所有 key
  419. *
  420. * @param array $detail 商品相关数据
  421. * @param array $activity 活动相关数据
  422. * @return array
  423. */
  424. public function keysActivity($detail, $activity)
  425. {
  426. // 获取 hash key
  427. $keyActivity = $this->keyActivity($activity['activity_id'], $activity['activity_type']);
  428. $keyGoodsSkuPrice = '';
  429. $keySale = '';
  430. if (isset($detail['goods_sku_price_id']) && $detail['goods_sku_price_id']) {
  431. // 获取 hash 表中商品 sku 的 key
  432. $keyGoodsSkuPrice = $this->keyActivityGoods($detail['goods_id'], $detail['goods_sku_price_id']);
  433. // 获取 hash 表中商品 sku 的 销量的 key
  434. $keySale = $this->keyActivityGoods($detail['goods_id'], $detail['goods_sku_price_id'], true);
  435. }
  436. // 需要拼团的字段
  437. $keyGroupon = '';
  438. $keyGrouponNum = '';
  439. $keyGrouponUserlist = '';
  440. if (isset($detail['groupon_id']) && $detail['groupon_id']) {
  441. // 获取 hash 表中团 key
  442. $keyGroupon = $this->keyActivityGroupon($detail['groupon_id'], $detail['goods_id']);
  443. // 获取 hash 表中团当前人数 key
  444. $keyGrouponNum = $this->keyActivityGroupon($detail['groupon_id'], $detail['goods_id'], 'num');
  445. // 获取 hash 表中团当前人员列表 key
  446. $keyGrouponUserlist = $this->keyActivityGroupon($detail['groupon_id'], $detail['goods_id'], 'users');
  447. }
  448. return compact('keyActivity', 'keyGoodsSkuPrice', 'keySale', 'keyGroupon', 'keyGrouponNum', 'keyGrouponUserlist');
  449. }
  450. // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 获取活动相关 key ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  451. }