StoreActivityServices.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\activity;
  13. use app\dao\activity\StoreActivityDao;
  14. use app\services\activity\seckill\StoreSeckillServices;
  15. use app\services\activity\seckill\StoreSeckillTimeServices;
  16. use app\services\BaseServices;
  17. use app\services\product\product\StoreDescriptionServices;
  18. use app\services\product\product\StoreProductServices;
  19. use app\services\product\sku\StoreProductAttrResultServices;
  20. use app\services\product\sku\StoreProductAttrServices;
  21. use app\services\product\sku\StoreProductAttrValueServices;
  22. use app\services\store\SystemStoreServices;
  23. use crmeb\exceptions\AdminException;
  24. use crmeb\services\CacheService;
  25. /**
  26. * 活动
  27. * Class StoreActivityServices
  28. * @package app\services\activity
  29. * @mixin StoreActivityDao
  30. */
  31. class StoreActivityServices extends BaseServices
  32. {
  33. /**
  34. * @var string[]
  35. */
  36. public $typeName = [
  37. 1 => '秒杀',
  38. ];
  39. /**
  40. * StoreActivityServices constructor.
  41. * @param StoreActivityDao $dao
  42. */
  43. public function __construct(StoreActivityDao $dao)
  44. {
  45. $this->dao = $dao;
  46. }
  47. /**
  48. * 活动列表
  49. * @param array $where
  50. * @return array
  51. * @throws \think\db\exception\DataNotFoundException
  52. * @throws \think\db\exception\DbException
  53. * @throws \think\db\exception\ModelNotFoundException
  54. */
  55. public function systemPage(array $where)
  56. {
  57. [$page, $limit] = $this->getPageValue();
  58. $list = $this->dao->getList($where, '*', $page, $limit, ['seckill' => function ($query) {
  59. $query->field('id,activity_id');
  60. }]);
  61. $count = $this->dao->count($where);
  62. /** @var StoreSeckillTimeServices $seckillTimeServices */
  63. $seckillTimeServices = app()->make(StoreSeckillTimeServices::class);
  64. $timeList = $seckillTimeServices->time_list();
  65. if ($timeList) {
  66. $timeList = array_combine(array_column($timeList, 'id'), $timeList);
  67. }
  68. foreach ($list as &$item) {
  69. $item['product_count'] = count($item['seckill']);
  70. $item['time_id'] = is_string($item['time_id']) ? explode(',', $item['time_id']) : $item['time_id'];
  71. $item['time_list'] = [];
  72. foreach ($item['time_id'] as $time_id) {
  73. if (isset($timeList[$time_id])) $item['time_list'][] = $timeList[$time_id];
  74. }
  75. if ($item['status']) {
  76. if ($item['start_day'] > time())
  77. $item['start_name'] = '未开始';
  78. else if (bcadd((string)$item['end_day'], '86400') < time())
  79. $item['start_name'] = '已结束';
  80. else if (bcadd((string)$item['end_day'], '86400') > time() && $item['start_day'] < time()) {
  81. $item['start_name'] = '进行中';
  82. }
  83. } else $item['start_name'] = '已结束';
  84. switch ($item['type']) {
  85. case 1://秒杀
  86. $item['start_time'] = substr_replace($item['start_time'], ':', 2, 0);
  87. $item['end_time'] = substr_replace($item['end_time'], ':', 2, 0);
  88. break;
  89. }
  90. $item['start_day'] = date('Y-m-d', $item['start_day']);
  91. $item['end_day'] = date('Y-m-d', $item['end_day']);
  92. // $item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
  93. }
  94. return compact('list', 'count');
  95. }
  96. /**
  97. * 获取一条活动信息
  98. * @param int $id
  99. * @param array $field
  100. * @return array
  101. * @throws \think\db\exception\DataNotFoundException
  102. * @throws \think\db\exception\DbException
  103. * @throws \think\db\exception\ModelNotFoundException
  104. */
  105. public function getInfo(int $id, array $field = ['*'])
  106. {
  107. $info = $this->dao->get($id, $field);
  108. if (!$info) {
  109. throw new AdminException('数据不存在');
  110. }
  111. $info = $info->toArray();
  112. return $info;
  113. }
  114. /**
  115. * 获取一条秒杀活动
  116. * @param int $id
  117. * @return array
  118. * @throws \think\db\exception\DataNotFoundException
  119. * @throws \think\db\exception\DbException
  120. * @throws \think\db\exception\ModelNotFoundException
  121. */
  122. public function getSeckillInfo(int $id)
  123. {
  124. $info = $this->getInfo($id);
  125. /** @var StoreSeckillServices $seckillServices */
  126. $seckillServices = app()->make(StoreSeckillServices::class);
  127. $seckill = $seckillServices->getList(['activity_id' => $id, 'is_del' => 0], 0, 0, ['attrValue']);
  128. $info['section_data'] = [date('Y-m-d', $info['start_day']), date('Y-m-d', $info['end_day'])];
  129. $productList = [];
  130. if ($seckill) {
  131. /** @var StoreProductServices $productServices */
  132. $productServices = app()->make(StoreProductServices::class);
  133. $productList = $productServices->searchList(['id' => array_column($seckill, 'product_id'), 'is_del' => 0, 'is_verify' => 1]);
  134. $productList = $productList['list'] ?? [];
  135. $seckill = array_combine(array_column($seckill, 'product_id'), $seckill);
  136. //放入秒杀商品价格
  137. foreach ($productList as &$product) {
  138. $seckillInfo = $seckill[$product['id']] ?? [];
  139. $attrValue = $product['attrValue'] ?? [];
  140. if ($seckillInfo && $attrValue) {
  141. $product['status'] = $seckillInfo['status'];
  142. $seckillAttrValue = $seckillInfo['attrValue'] ?? [];
  143. if ($seckillAttrValue) {
  144. $seckillAttrValue = array_combine(array_column($seckillAttrValue, 'suk'), $seckillAttrValue);
  145. foreach ($attrValue as &$value) {
  146. $value['quota'] = $seckillAttrValue[$value['suk']]['quota'] ?? 0;
  147. $value['quota_show'] = $seckillAttrValue[$value['suk']]['quota_show'] ?? 0;
  148. $value['price'] = $seckillAttrValue[$value['suk']]['price'] ?? 0;
  149. $value['cost'] = $seckillAttrValue[$value['suk']]['cost'] ?? 0;
  150. $value['ot_price'] = $seckillAttrValue[$value['suk']]['ot_price'] ?? 0;
  151. }
  152. $product['attrValue'] = $attrValue;
  153. }
  154. }
  155. }
  156. }
  157. $info['productList'] = $productList;
  158. //适用门店
  159. $info['stores'] = [];
  160. if (isset($info['applicable_type']) && ($info['applicable_type'] == 1 || ($info['applicable_type'] == 2 && isset($info['applicable_store_id']) && $info['applicable_store_id']))) {//查询门店信息
  161. $where = ['is_del' => 0];
  162. if ($info['applicable_type'] == 2) {
  163. $store_ids = is_array($info['applicable_store_id']) ? $info['applicable_store_id'] : explode(',', $info['applicable_store_id']);
  164. $where['id'] = $store_ids;
  165. }
  166. $field = ['id', 'cate_id', 'name', 'phone', 'address', 'detailed_address', 'image', 'is_show', 'day_time', 'day_start', 'day_end'];
  167. /** @var SystemStoreServices $storeServices */
  168. $storeServices = app()->make(SystemStoreServices::class);
  169. $storeData = $storeServices->getStoreList($where, $field, '', '', 0, ['categoryName']);
  170. $info['stores'] = $storeData['list'] ?? [];
  171. }
  172. return $info;
  173. }
  174. /**
  175. * 保存活动信息
  176. * @param int $id
  177. * @param array $data
  178. * @param array $expend
  179. * @param int $type
  180. * @return bool
  181. */
  182. public function saveData(int $id, array $data, array $expend, int $type = 1)
  183. {
  184. if (!$data || !$expend) {
  185. throw new AdminException('缺少活动数据');
  186. }
  187. $section_data = $data['section_data'];
  188. $data['start_day'] = strtotime($section_data[0]);
  189. $data['end_day'] = strtotime($section_data[1]);
  190. unset($data['section_data']);
  191. $this->transaction( function () use ($id, $type, $data, $section_data, $expend) {
  192. if ($id) {
  193. $this->getInfo($id);
  194. $this->dao->update($id, $data);
  195. $this->clearAcivityAttr($id, $type);
  196. } else {
  197. $data['add_time'] = time();
  198. $res = $this->dao->save($data);
  199. $id = (int)$res->id;
  200. }
  201. //处理活动商品
  202. switch ($type) {
  203. case 1:
  204. $data['section_data'] = $section_data;
  205. $this->saveActivitySeckill($id, $expend, $data);
  206. break;
  207. }
  208. });
  209. return true;
  210. }
  211. /**
  212. * 保存秒杀商品
  213. * @param int $id
  214. * @param array $data
  215. * @param array $activity_data
  216. * @return bool
  217. */
  218. public function saveActivitySeckill(int $id, array $data, array $activity_data)
  219. {
  220. $productIds = array_column($data, 'id');
  221. /** @var StoreProductServices $productServices */
  222. $productServices = app()->make(StoreProductServices::class);
  223. $productList = $productServices->searchList(['id' => $productIds, 'is_del' => 0, 'is_verify' => 1]);
  224. $productList = $productList['list'] ?? [];
  225. //放入秒杀商品价格
  226. /** @var StoreSeckillServices $seckillServices */
  227. $seckillServices = app()->make(StoreSeckillServices::class);
  228. $data = array_combine($productIds, $data);
  229. foreach ($productList as &$product) {
  230. $attrInfo = $productServices->getProductRules((int)$product['id'], 0);
  231. $seckillData = [];
  232. $seckillData['activity_id'] = $id;
  233. $seckillData['product_id'] = $product['id'] ?? 0;
  234. $seckillData['title'] = $product['store_name'] ?? '';
  235. $seckillData['info'] = $product['store_info'] ?? '';
  236. $seckillData['unit_name'] = $product['unit_name'] ?? '';
  237. $seckillData['section_time'] = $activity_data['section_data'];
  238. $seckillData['images'] = $product['slider_image'] ?? '';
  239. $seckillData['description'] = $product['description'] ?? '';
  240. $seckillData['status'] = $data[$product['id']]['status'] ?? 1;
  241. $seckillData['time_id'] = $activity_data['time_id'] ?? [];
  242. $seckillData['num'] = $activity_data['num'] ?? 0;
  243. $seckillData['once_num'] = $activity_data['once_num'] ?? 0;
  244. $seckillData['delivery_type'] = $product['delivery_type'] ?? '';
  245. $seckillData['temp_id'] = $product['temp_id'];//运费设置
  246. $seckillData['freight'] = $product['freight'];//运费设置
  247. $seckillData['postage'] = $product['freight'];//邮费
  248. $seckillData['custom_form'] = $product['custom_form'];//自定义表单
  249. $seckillData['system_form_id'] = $product['system_form_id'];//系统表单ID
  250. $seckillData['product_type'] = $product['product_type'];//商品类型
  251. $seckillData['applicable_type'] = $activity_data['applicable_type'];//适用门店类型
  252. $seckillData['applicable_store_id'] = $activity_data['applicable_store_id'];//适用门店IDS
  253. $seckillData['items'] = $attrInfo['items'];
  254. $attrs = $attrInfo['attrs'] ?? [];
  255. if ($attrs) {
  256. $seckillAttrValue = $data[$product['id']]['attrValue'] ?? [];
  257. if (!$seckillAttrValue) {
  258. throw new AdminException('请选择商品规格');
  259. }
  260. foreach ($seckillAttrValue as $sattr) {
  261. if (!isset($sattr['price']) || !$sattr['price']) {
  262. throw new AdminException('请填写商品('.$product['store_name'] .')活动价');
  263. }
  264. if (!isset($sattr['quota']) || !$sattr['quota']) {
  265. throw new AdminException('请填写商品('.$product['store_name'] .')限量');
  266. }
  267. }
  268. $seckillAttrValue = array_combine(array_column($seckillAttrValue, 'suk'), $seckillAttrValue);
  269. foreach ($attrs as $attr) {
  270. $sku = implode(',', $attr['detail']);
  271. if(!isset($seckillAttrValue[$sku])) {
  272. throw new AdminException('请重新选择商品规格');
  273. }
  274. if (($seckillAttrValue[$sku][''] ?? 0) > $attr['stock']) {
  275. throw new AdminException('限量超过了商品库存');
  276. }
  277. $attr['quota'] = $attr['quota_show'] = $seckillAttrValue[$sku]['quota'] ?? 0;
  278. $attr['price'] = $seckillAttrValue[$sku]['price'] ?? 0;
  279. $attr['cost'] = $seckillAttrValue[$sku]['cost'] ?? 0;
  280. $attr['ot_price'] = $seckillAttrValue[$sku]['ot_price'] ?? 0;
  281. $seckillData['attrs'][] = $attr;
  282. }
  283. }
  284. $seckillServices->saveData(0, $seckillData);
  285. }
  286. return true;
  287. }
  288. /**
  289. * 清空活动商品数据
  290. * @param int $id
  291. * @return bool
  292. * @throws \think\db\exception\DataNotFoundException
  293. * @throws \think\db\exception\DbException
  294. * @throws \think\db\exception\ModelNotFoundException
  295. */
  296. public function clearAcivityAttr(int $id, int $type = 1)
  297. {
  298. /** @var StoreSeckillServices $seckillServices */
  299. $seckillServices = app()->make(StoreSeckillServices::class);
  300. $seckill = $seckillServices->getList(['activity_id' => $id, 'is_del' => 0]);
  301. if ($seckill) {
  302. $seckillIds = array_column($seckill, 'id');
  303. /** @var StoreProductAttrResultServices $storeProductAttrResultServices */
  304. $storeProductAttrResultServices = app()->make(StoreProductAttrResultServices::class);
  305. /** @var StoreDescriptionServices $storeDescriptionServices */
  306. $storeDescriptionServices = app()->make(StoreDescriptionServices::class);
  307. /** @var StoreProductAttrServices $storeProductAttrServices */
  308. $storeProductAttrServices = app()->make(StoreProductAttrServices::class);
  309. /** @var StoreProductAttrValueServices $storeProductAttrValueServices */
  310. $storeProductAttrValueServices = app()->make(StoreProductAttrValueServices::class);
  311. $where = ['product_id' => $seckillIds, 'type' => $type];
  312. $storeProductAttrResultServices->delete($where);
  313. $storeDescriptionServices->delete($where);
  314. $storeProductAttrServices->delete($where);
  315. $storeProductAttrValueServices->delete($where);
  316. $seckillServices->delete(['activity_id' => $id, 'is_del' => 0]);
  317. $seckillServices->cacheTag()->clear();
  318. }
  319. CacheService::redisHandler('product_attr')->clear();
  320. return true;
  321. }
  322. /**
  323. * 判断时间段是否存在秒杀活动
  324. * @param array $where
  325. * @param int $type
  326. * @return bool
  327. * @throws \think\db\exception\DbException
  328. */
  329. public function existenceActivity(array $where, int $type = 1): bool
  330. {
  331. $where['is_del'] = 0;
  332. if (!isset($where['type'])) $where['type'] = $type;
  333. return $this->dao->checkActivity($where);
  334. }
  335. }