StoreSeckill.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. namespace app\models\store;
  3. use app\admin\model\store\StoreProductAttrValue as StoreProductAttrValueModel;
  4. use crmeb\basic\BaseModel;
  5. use app\admin\model\store\StoreProductAttrValue;
  6. /**
  7. * TODO 秒杀产品Model
  8. * Class StoreSeckill
  9. * @package app\models\store
  10. */
  11. class StoreSeckill extends BaseModel
  12. {
  13. /**
  14. * 数据表主键
  15. * @var string
  16. */
  17. protected $pk = 'id';
  18. /**
  19. * 模型名称
  20. * @var string
  21. */
  22. protected $name = 'store_seckill';
  23. protected function getImagesAttr($value)
  24. {
  25. return json_decode($value, true) ?: [];
  26. }
  27. public function getDescriptionAttr($value)
  28. {
  29. return htmlspecialchars_decode($value);
  30. }
  31. public static function getSeckillCount()
  32. {
  33. $seckillTime = sys_data('routine_seckill_time') ?: []; //秒杀时间段
  34. $timeInfo = ['time' => 0, 'continued' => 0];
  35. foreach ($seckillTime as $key => $value) {
  36. $currentHour = date('H');
  37. $activityEndHour = bcadd((int)$value['time'], (int)$value['continued'], 0);
  38. if ($currentHour >= (int)$value['time'] && $currentHour < $activityEndHour && $activityEndHour < 24) {
  39. $timeInfo = $value;
  40. break;
  41. }
  42. }
  43. if ($timeInfo['time'] == 0) return 0;
  44. $activityEndHour = bcadd((int)$timeInfo['time'], (int)$timeInfo['continued'], 0);
  45. $startTime = bcadd(strtotime(date('Y-m-d')), bcmul($timeInfo['time'], 3600, 0));
  46. $stopTime = bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  47. return self::where('is_del', 0)->where('status', 1)->where('start_time', '<=', $startTime)->where('stop_time', '>=', $stopTime)->count();
  48. }
  49. /**
  50. * 获取秒杀列表
  51. * @param $time
  52. * @param int $page
  53. * @param int $limit
  54. * @return array
  55. * @throws \think\db\exception\DataNotFoundException
  56. * @throws \think\db\exception\DbException
  57. * @throws \think\db\exception\ModelNotFoundException
  58. */
  59. public static function seckillList($time, $page = 0, $limit = 20)
  60. {
  61. if ($page) $list = StoreSeckill::alias('n')->join('store_product c', 'c.id=n.product_id')->where('c.is_show', 1)->where('c.is_del', 0)->where('n.is_del', 0)->where('n.status', 1)->where('n.start_time', '<=', time())->where('n.stop_time', '>=', time() - 86400)->where('n.time_id', $time)->field('n.*')->order('n.sort desc')->page($page, $limit)->select();
  62. else $list = StoreSeckill::alias('n')->join('store_product c', 'c.id=n.product_id')->where('c.is_show', 1)->where('c.is_del', 0)->where('n.is_del', 0)->where('n.status', 1)->where('n.start_time', '<=', time())->where('n.stop_time', '>=', time() - 86400)->where('n.time_id', $time)->field('n.*')->order('sort desc')->select();
  63. if ($list) return $list->hidden(['cost', 'add_time', 'is_del'])->toArray();
  64. return [];
  65. }
  66. /**
  67. * 获取所有秒杀产品
  68. * @param string $field
  69. * @return array
  70. */
  71. public static function getListAll($offset = 0, $limit = 10, $field = 'id,product_id,image,title,price,ot_price,start_time,stop_time,stock,sales')
  72. {
  73. $time = time();
  74. $model = self::where('is_del', 0)->where('status', 1)->where('stock', '>', 0)->field($field)
  75. ->where('start_time', '<', $time)->where('stop_time', '>', $time)->order('sort DESC,add_time DESC');
  76. $model = $model->limit($offset, $limit);
  77. $list = $model->select();
  78. if ($list) return $list->toArray();
  79. else return [];
  80. }
  81. /**
  82. * 获取热门推荐的秒杀产品
  83. * @param int $limit
  84. * @param string $field
  85. * @return array
  86. */
  87. public static function getHotList($limit = 0, $field = 'id,product_id,image,title,price,ot_price,start_time,stop_time,stock')
  88. {
  89. $time = time();
  90. $model = self::where('is_hot', 1)->where('is_del', 0)->where('status', 1)->where('stock', '>', 0)->field($field)
  91. ->where('start_time', '<', $time)->where('stop_time', '>', $time)->order('sort DESC,add_time DESC');
  92. if ($limit) $model->limit($limit);
  93. $list = $model->select();
  94. if ($list) return $list->toArray();
  95. else return [];
  96. }
  97. /**
  98. * 获取一条秒杀产品
  99. * @param $id
  100. * @param string $field
  101. * @return array|false|\PDOStatement|string|\think\Model
  102. */
  103. public static function getValidProduct($id, $field = '*')
  104. {
  105. $time = time();
  106. $info = self::alias('n')->join('store_product c', 'c.id=n.product_id')->where('n.id', $id)->where('c.is_show', 1)->where('c.is_del', 0)->where('n.is_del', 0)->where('n.status', 1)->where('n.start_time', '<', $time)->where('n.stop_time', '>', $time - 86400)->field('n.*,SUM(c.sales+c.ficti) as total')->find();
  107. if ($info['id']) {
  108. return $info;
  109. } else {
  110. return [];
  111. }
  112. }
  113. /**
  114. * 获取秒杀是否有开启
  115. * @return bool
  116. */
  117. public static function getSeckillContStatus()
  118. {
  119. $time = time();
  120. $count = self::where('is_del', 0)->where('status', 1)->where('start_time', '<', $time)->where('stop_time', '>', $time)->count();
  121. return $count ? true : false;
  122. }
  123. public static function initFailSeckill()
  124. {
  125. self::where('is_hot', 1)->where('is_del', 0)->where('status', '<>', 1)->where('stop_time', '<', time())->update(['status' => '-1']);
  126. }
  127. public static function idBySimilaritySeckill($id, $limit = 4, $field = '*')
  128. {
  129. $time = time();
  130. $list = [];
  131. $productId = self::where('id', $id)->value('product_id');
  132. if ($productId) {
  133. $list = array_merge($list, self::where('product_id', $productId)->where('id', '<>', $id)
  134. ->where('is_del', 0)->where('status', 1)->where('stock', '>', 0)
  135. ->field($field)->where('start_time', '<', $time)->where('stop_time', '>', $time)
  136. ->order('sort DESC,add_time DESC')->limit($limit)->select()->toArray());
  137. }
  138. $limit = $limit - count($list);
  139. if ($limit) {
  140. $list = array_merge($list, self::getHotList($limit, $field));
  141. }
  142. return $list;
  143. }
  144. /** 获取秒杀产品库存
  145. * @param $id
  146. * @return mixed
  147. */
  148. public static function getProductStock($id)
  149. {
  150. return self::where('id', $id)->value('stock');
  151. }
  152. /**
  153. * 获取字段值
  154. * @param $id
  155. * @param string $field
  156. * @return mixed
  157. */
  158. public static function getProductField($id, $field = 'title')
  159. {
  160. return self::where('id', $id)->value($field);
  161. }
  162. /**
  163. * 修改秒杀库存
  164. * @param int $num
  165. * @param int $seckillId
  166. * @return bool
  167. */
  168. public static function decSeckillStock($num = 0, $seckillId = 0, $unique = '')
  169. {
  170. $product_id = self::where('id', $seckillId)->value('product_id');
  171. if ($unique) {
  172. $res = false !== StoreProductAttrValue::decProductAttrStock($seckillId, $unique, $num, 1);
  173. $res = $res && self::where('id', $seckillId)->dec('stock', $num)->dec('quota', $num)->inc('sales', $num)->update();
  174. $sku = StoreProductAttrValue::where('product_id', $seckillId)->where('unique', $unique)->where('type', 1)->value('suk');
  175. $res = $res && StoreProductAttrValue::where('product_id', $product_id)->where('suk', $sku)->where('type', 0)->dec('stock', $num)->inc('sales', $num)->update();
  176. } else {
  177. $res = false !== self::where('id', $seckillId)->dec('stock', $num)->inc('sales', $num)->update();
  178. }
  179. $res = $res && StoreProduct::where('id', $product_id)->dec('stock', $num)->inc('sales', $num)->update();
  180. return $res;
  181. }
  182. /**
  183. * 增加库存较少销量
  184. * @param int $num
  185. * @param int $seckillId
  186. * @return bool
  187. */
  188. public static function incSeckillStock($num = 0, $seckillId = 0, $unique = '')
  189. {
  190. $seckill = self::where('id', $seckillId)->field(['product_id', 'stock', 'sales', 'quota'])->find();
  191. if (!$seckill) return true;
  192. if ($seckill->sales > 0) $seckill->sales = bcsub($seckill->sales, $num, 0);
  193. if ($seckill->sales < 0) $seckill->sales = 0;
  194. $res = true;
  195. if ($unique) {
  196. $res = false !== StoreProductAttrValueModel::incProductAttrStock($seckillId, $unique, $num, 1);
  197. $sku = StoreProductAttrValue::where('product_id', $seckillId)->where('unique', $unique)->where('type', 1)->value('suk');
  198. $res = $res && StoreProductAttrValue::where('product_id', $seckill['product_id'])->where('suk', $sku)->where('type', 0)->inc('stock', $num)->dec('sales', $num)->update();
  199. }
  200. $seckill->stock = bcadd($seckill->stock, $num, 0);
  201. $seckill->quota = bcadd($seckill->quota, $num, 0);
  202. $res = $res && $seckill->save() && StoreProduct::where('id', $seckill['product_id'])->inc('stock', $num)->dec('sales', $num)->update();
  203. return $res;
  204. }
  205. /**
  206. * 获取秒杀是否已结束
  207. * @param $seckill_id
  208. * @return bool
  209. */
  210. public static function isSeckillEnd($seckill_id)
  211. {
  212. $time_id = self::where('id', $seckill_id)->value('time_id');
  213. //秒杀时间段
  214. $seckillTime = sys_data('routine_seckill_time') ?? [];
  215. $seckillTimeIndex = 0;
  216. $activityTime = [];
  217. if (count($seckillTime)) {
  218. foreach ($seckillTime as $key => &$value) {
  219. $currentHour = date('H');
  220. $activityEndHour = bcadd((int)$value['time'], (int)$value['continued'], 0);
  221. if ($activityEndHour > 24) {
  222. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  223. $value['state'] = '即将开始';
  224. $value['status'] = 2;
  225. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  226. } else {
  227. if ($currentHour >= (int)$value['time'] && $currentHour < $activityEndHour) {
  228. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  229. $value['state'] = '抢购中';
  230. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  231. $value['status'] = 1;
  232. if (!$seckillTimeIndex) $seckillTimeIndex = $key;
  233. } else if ($currentHour < (int)$value['time']) {
  234. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  235. $value['state'] = '即将开始';
  236. $value['status'] = 2;
  237. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  238. } else if ($currentHour >= $activityEndHour) {
  239. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  240. $value['state'] = '已结束';
  241. $value['status'] = 0;
  242. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  243. }
  244. }
  245. if ($value['id'] == $time_id) {
  246. $activityTime = $value;
  247. break;
  248. }
  249. }
  250. }
  251. if (time() < $activityTime['stop'])
  252. return true;
  253. else
  254. return false;
  255. }
  256. /**
  257. * 检查秒杀活动状态
  258. * @param $seckill_id
  259. * @return bool
  260. */
  261. public static function checkStatus($seckill_id)
  262. {
  263. $time_id = self::where('id', $seckill_id)->value('time_id');
  264. //秒杀时间段
  265. $seckillTime = sys_data('routine_seckill_time') ?? [];
  266. $seckillTimeIndex = 0;
  267. $activityTime = [];
  268. if (count($seckillTime)) {
  269. foreach ($seckillTime as $key => &$value) {
  270. $currentHour = date('H');
  271. $activityEndHour = bcadd((int)$value['time'], (int)$value['continued'], 0);
  272. if ($activityEndHour > 24) {
  273. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  274. $value['state'] = '即将开始';
  275. $value['status'] = 2;
  276. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  277. } else {
  278. if ($currentHour >= (int)$value['time'] && $currentHour < $activityEndHour) {
  279. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  280. $value['state'] = '抢购中';
  281. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  282. $value['status'] = 1;
  283. if (!$seckillTimeIndex) $seckillTimeIndex = $key;
  284. } else if ($currentHour < (int)$value['time']) {
  285. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  286. $value['state'] = '即将开始';
  287. $value['status'] = 2;
  288. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  289. } else if ($currentHour >= $activityEndHour) {
  290. $value['time'] = strlen((int)$value['time']) == 2 ? (int)$value['time'] . ':00' : '0' . (int)$value['time'] . ':00';
  291. $value['state'] = '已结束';
  292. $value['status'] = 0;
  293. $value['stop'] = (int)bcadd(strtotime(date('Y-m-d')), bcmul($activityEndHour, 3600, 0));
  294. }
  295. }
  296. if ($value['id'] == $time_id) {
  297. $activityTime = $value;
  298. break;
  299. }
  300. }
  301. }
  302. return $activityTime;
  303. }
  304. }