Coupon.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <?php
  2. namespace app\common\model;
  3. use think\Model;
  4. use addons\shop\model\UserCoupon;
  5. use addons\shop\library\coupon\ToCalculate;
  6. use addons\shop\library\coupon\Discount;
  7. use addons\shop\library\coupon\FullReduction;
  8. use addons\shop\model\Order;
  9. use addons\shop\library\IntCode;
  10. class Coupon extends Model
  11. {
  12. // 表名
  13. protected $name = 'shop_coupon';
  14. // 自动写入时间戳字段
  15. protected $autoWriteTimestamp = 'int';
  16. // 定义时间戳字段名
  17. protected $createTime = 'createtime';
  18. protected $updateTime = 'updatetime';
  19. protected $deleteTime = false;
  20. // 追加属性
  21. protected $append = [
  22. 'condition_text',
  23. 'result_text',
  24. 'is_open_text',
  25. 'url',
  26. 'use_times_text',
  27. 'receive_times',
  28. ];
  29. protected static $config = [];
  30. protected static function init()
  31. {
  32. $config = get_addon_config('shop');
  33. self::$config = $config;
  34. }
  35. public function getUrlAttr($value, $data)
  36. {
  37. return $this->buildUrl($value, $data);
  38. }
  39. public function getFullurlAttr($value, $data)
  40. {
  41. return $this->buildUrl($value, $data, true);
  42. }
  43. private function buildUrl($value, $data, $domain = false)
  44. {
  45. $vars = [
  46. ':coupon' => $data['id']
  47. ];
  48. $suffix = static::$config['moduleurlsuffix']['coupon'] ?? static::$config['urlsuffix'];
  49. return addon_url('shop/coupon/show', $vars, $suffix, $domain);
  50. }
  51. public function getReceiveTimesAttr($value, $data)
  52. {
  53. return date("Y-m-d H:i:s", $data['begintime']) . ' - ' . date("Y-m-d H:i:s", $data['endtime']);
  54. }
  55. public function getConditionList()
  56. {
  57. return ['0' => __('Condition 0'), '1' => __('Condition 1'), '2' => __('Condition 2'), '3' => __('Condition 3'), '4' => __('Condition 4')];
  58. }
  59. public function getResultList()
  60. {
  61. return ['0' => __('Result 0'), '1' => __('Result 1')];
  62. }
  63. public function getIsOpenList()
  64. {
  65. return ['0' => __('Is_open 0'), '1' => __('Is_open 1')];
  66. }
  67. public function getConditionTextAttr($value, $data)
  68. {
  69. $value = $value ?: ($data['condition'] ?? '');
  70. $list = $this->getConditionList();
  71. return $list[$value] ?? '';
  72. }
  73. public function getResultTextAttr($value, $data)
  74. {
  75. $value = $value ?: ($data['result'] ?? '');
  76. $list = $this->getResultList();
  77. return $list[$value] ?? '';
  78. }
  79. public function getIsOpenTextAttr($value, $data)
  80. {
  81. $value = $value ?: ($data['is_open'] ?? '');
  82. $list = $this->getIsOpenList();
  83. return $list[$value] ?? '';
  84. }
  85. public function getUseTimesTextAttr($value, $data)
  86. {
  87. $value = $value ?: ($data['use_times'] ?? '');
  88. return preg_replace('/\s\d{2}\:\d{2}\:\d{2}/i', '', $value);
  89. }
  90. public function getResultDataAttr($value)
  91. {
  92. return (array)json_decode($value, true);
  93. }
  94. //购买计算:0=订单满xx打x折,1=订单满xx减x元
  95. public function doBuy($money)
  96. {
  97. $obj = null;
  98. //判断是那种计算方式
  99. switch ((int)$this->getData('result')) {
  100. case 0: //订单满xx打x折
  101. $obj = new ToCalculate(new Discount());
  102. break;
  103. case 1: //订单满xx减x元
  104. $obj = new ToCalculate(new FullReduction());
  105. break;
  106. }
  107. if (!$obj) {
  108. throw new \Exception('未找到优惠方式');
  109. }
  110. return $obj->doing($this->getData('result_data'), $money);
  111. }
  112. /**
  113. * @ 获取优惠券
  114. * @param $id
  115. * @return array|bool|Model|\PDOStatement|string|null
  116. */
  117. public static function getCouponInfo($id)
  118. {
  119. if (!is_numeric($id)) {
  120. $id = IntCode::decode($id);
  121. }
  122. $row = self::where('id', $id)->where('is_open', 1)->find();
  123. if (!empty($row)) {
  124. $row->id = IntCode::encode($row->id);
  125. $rd = $row->result_data;
  126. $row->result_tips = '订单满' . $rd['money'] . ($row->result ? '减' : '打') . $rd['number'] . ($row->result ? '元' : '折');
  127. if ($row->mode == 'fixation') {
  128. $row->expire_time = "领取后 {$row['use_times']} 天内有效";
  129. } else {
  130. $row->expire_time = "领取后有效时间为:" . $row['use_times_text'];
  131. }
  132. }
  133. return $row;
  134. }
  135. /**
  136. * 获取优惠券
  137. * @param $coupon_id
  138. * @return array|false|\PDOStatement|string|Model|$this
  139. */
  140. public function getCoupon($coupon_id)
  141. {
  142. return $this->where('id', $coupon_id)->where('is_open', 1)->find();
  143. }
  144. /**
  145. * 检查优惠券
  146. * @return $this
  147. * @throws \Exception
  148. */
  149. public function checkCoupon()
  150. {
  151. if (empty($this->origin)) {
  152. throw new \Exception('该优惠券不存在!');
  153. }
  154. return $this;
  155. }
  156. /**
  157. * 是否开启
  158. * @return $this
  159. * @throws \Exception
  160. */
  161. public function checkOpen()
  162. {
  163. if ($this->getData('is_open') == 0) {
  164. throw new \Exception('该优惠券已关闭!');
  165. }
  166. return $this;
  167. }
  168. /**
  169. * 判断优惠券的剩余数量
  170. * @return $this
  171. * @throws \Exception
  172. */
  173. public function checkNumber()
  174. {
  175. if ($this->getData('give_num') <= $this->getData('received_num')) {
  176. throw new \Exception('该优惠券已被领完!');
  177. }
  178. return $this;
  179. }
  180. /**
  181. * 判断自己的剩余数量
  182. * @param $user_id
  183. * @return $this
  184. * @throws \think\Exception
  185. */
  186. public function checkMyNumber($user_id)
  187. {
  188. $self_num = UserCoupon::where('user_id', $user_id)->where('coupon_id', $this->getData('id'))->count();
  189. if ($this->getData('allow_num') <= $self_num) {
  190. throw new \Exception('您没有可领取该优惠券的数量!');
  191. }
  192. return $this;
  193. }
  194. /**
  195. * 校验优惠券领取的时间
  196. * @return $this
  197. * @throws \Exception
  198. */
  199. public function checkReceiveTime()
  200. {
  201. $time = time();
  202. $receive_times = $this->getAttr('receive_times');
  203. $range_time = explode(' - ', $receive_times);
  204. if (count($range_time) != 2) {
  205. throw new \Exception('该优惠券领取时间格式错误!', 1000);
  206. }
  207. if ($time < strtotime($range_time[0])) {
  208. throw new \Exception('未到领取优惠券时间,请过后再来!', 1002);
  209. }
  210. if ($time > strtotime($range_time[1])) {
  211. throw new \Exception('该优惠券已经失效!', 1000);
  212. }
  213. return $this;
  214. }
  215. /**
  216. * 校验优惠券使用的时间
  217. * @param $use_coupon_time
  218. * @return $this
  219. * @throws \Exception
  220. */
  221. public function checkUseTime($use_coupon_time)
  222. {
  223. $time = time();
  224. $use_times = $this->getData('use_times');
  225. if ($this->getData('mode') == 'fixation') { //固定日期
  226. if (!empty($use_times) && is_numeric($use_times) && ($time - $use_coupon_time >= $use_times * 24 * 60 * 60)) {
  227. throw new \Exception('该优惠券已经失效!', 1000);
  228. }
  229. } else { //时间范围
  230. $range_time = explode(' - ', $use_times);
  231. if (count($range_time) != 2) {
  232. throw new \Exception('该优惠券使用时间格式错误!', 1000);
  233. }
  234. if ($time < strtotime($range_time[0])) {
  235. throw new \Exception('未到使用优惠券时间,请换个优惠券试试!', 1001);
  236. }
  237. if ($time > strtotime($range_time[1])) {
  238. throw new \Exception('该优惠券已经失效!', 1000);
  239. }
  240. }
  241. return $this;
  242. }
  243. /**
  244. * @ 获取生效和失效时间
  245. * @return array
  246. */
  247. public function getUseTime()
  248. {
  249. $timeArr = [];
  250. $time = time();
  251. $use_times = $this->getData('use_times');
  252. if ($this->getData('mode') == 'fixation') { //固定日期
  253. $timeArr[0] = $time;
  254. $timeArr[1] = \fast\Date::unixtime('day', $use_times, 'end');
  255. } else { //时间范围
  256. $range_time = explode(' - ', $use_times);
  257. if (count($range_time) != 2) {
  258. throw new \Exception('该优惠券使用时间格式错误!');
  259. }
  260. $timeArr[0] = strtotime($range_time[0]);
  261. $timeArr[1] = strtotime($range_time[1]);
  262. }
  263. return $timeArr;
  264. }
  265. /**
  266. * 优惠券是否已经领取
  267. * @param $user_id
  268. * @return $this
  269. */
  270. public function checkReceive($user_id)
  271. {
  272. if (empty($this->origin)) {
  273. throw new \Exception('该优惠券不存在!');
  274. }
  275. $row = UserCoupon::where('coupon_id', $this->getData('id'))->where('user_id', $user_id)->find();
  276. if ($row) {
  277. throw new \Exception('您已领取该优惠券!');
  278. }
  279. return $this;
  280. }
  281. /**
  282. * 判断条件:1指定商品
  283. * @param $goods_ids
  284. * @param $user_id
  285. * @param array $category_ids
  286. * @param array $brand_ids
  287. * @return $this
  288. * @throws \think\Exception
  289. */
  290. public function checkConditionGoods($goods_ids, $user_id, $category_ids = [], $brand_ids = [])
  291. {
  292. if (!empty($this->getData('condition_ids'))) {
  293. $conditions = CouponCondition::where('id', 'IN', $this->getData('condition_ids'))->select();
  294. foreach ($conditions as $item) {
  295. switch ($item['type']) {
  296. case 1: //指定商品
  297. $ids = explode(',', $item['content']);
  298. $result = array_intersect($goods_ids, $ids);
  299. //要全等
  300. if (count($goods_ids) != count($result)) {
  301. throw new \Exception('该优惠券必须在指定的商品使用!');
  302. }
  303. break;
  304. case 2: //新用户专享
  305. $order = (new Order())->where('user_id', $user_id)->where('paytime', '>', 0)->count();
  306. if ($order) {
  307. throw new \Exception('该优惠券限定新用户专享!');
  308. }
  309. break;
  310. case 3: //老用户专享
  311. $order = (new Order())->where('user_id', $user_id)->where('paytime', '>', 0)->count();
  312. if (!$order) {
  313. throw new \Exception('该优惠券限定老用户专享!');
  314. }
  315. break;
  316. case 4: //指定分类
  317. $ids = explode(',', $item['content']);
  318. $result = array_intersect($category_ids, $ids);
  319. //要全等
  320. if (count($category_ids) != count($result)) {
  321. throw new \Exception('该优惠券必须在指定分类的商品使用!');
  322. }
  323. break;
  324. case 5: //指定品牌
  325. $ids = explode(',', $item['content']);
  326. $result = array_intersect($brand_ids, $ids);
  327. //要全等
  328. if (count($brand_ids) != count($result)) {
  329. throw new \Exception('该优惠券必须在指定品牌的商品使用!');
  330. }
  331. break;
  332. }
  333. }
  334. }
  335. return $this;
  336. }
  337. /**
  338. * 渲染优惠券
  339. * @param $row
  340. * @param array $coupon_ids
  341. * @return mixed
  342. */
  343. public static function render(&$row, $coupon_ids = [])
  344. {
  345. //失效的优惠券状态
  346. if (!empty($coupon_ids) && in_array($row['id'], $coupon_ids)) {
  347. $num = 0;
  348. foreach ($coupon_ids as $res) {
  349. if ($res == $row['id']) {
  350. $num++;
  351. }
  352. }
  353. $row['is_received'] = ($row['allow_num'] - $num) == 0;
  354. } else {
  355. $row['is_received'] = false;
  356. }
  357. $row->expired = false;
  358. $row->online = true;
  359. try {
  360. $row->checkReceiveTime();
  361. } catch (\Exception $e) {
  362. if ($e->getCode() == 1000) {
  363. $row->expired = true;
  364. } elseif ($e->getCode() == 1002) {
  365. $row->online = false;
  366. };
  367. $row->message = $e->getMessage();
  368. }
  369. $row->has_more = $row->received_num >= $row->give_num;
  370. $row->id = IntCode::encode($row->id);
  371. return $row;
  372. }
  373. /**
  374. * 获取列表
  375. * @param $param
  376. * @return \think\Paginator
  377. */
  378. public static function tableList($param)
  379. {
  380. $pageNum = 15;
  381. if (!empty($param['num'])) {
  382. $pageNum = $param['num'];
  383. }
  384. return self::where(function ($query) use ($param) {
  385. if (!empty($param['is_private'])) {
  386. $query->where('is_private', $param['is_private']);
  387. }
  388. if (!empty($param['is_open'])) {
  389. $query->where('is_open', $param['is_open']);
  390. }
  391. if (isset($param['result']) && $param['result'] != '') {
  392. $query->where('result', $param['result']);
  393. }
  394. })->where('endtime', '>', time())->order('createtime desc')->paginate($pageNum);
  395. }
  396. }