StoreSeckill.php 14 KB

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