BargainLogic.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeshop100%开源免费商用商城系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | gitee下载:https://gitee.com/likeshop_gitee
  10. // | github下载:https://github.com/likeshop-github
  11. // | 访问官网:https://www.likeshop.cn
  12. // | 访问社区:https://home.likeshop.cn
  13. // | 访问手册:http://doc.likeshop.cn
  14. // | 微信公众号:likeshop技术社区
  15. // | likeshop团队 版权所有 拥有最终解释权
  16. // +----------------------------------------------------------------------
  17. // | author: likeshopTeam
  18. // +----------------------------------------------------------------------
  19. namespace app\shopapi\logic;
  20. use app\common\enum\BargainEnum;
  21. use app\common\enum\CouponEnum;
  22. use app\common\enum\DeliveryEnum;
  23. use app\common\enum\OrderEnum;
  24. use app\common\enum\OrderLogEnum;
  25. use app\common\logic\BaseLogic;
  26. use app\common\model\BargainActivity;
  27. use app\common\model\BargainGoods;
  28. use app\common\model\BargainHelp;
  29. use app\common\model\BargainInitiate;
  30. use app\common\model\Coupon;
  31. use app\common\model\CouponList;
  32. use app\common\model\Goods;
  33. use app\common\model\GoodsItem;
  34. use app\common\model\GoodsSpec;
  35. use app\common\model\GoodsSpecValue;
  36. use app\common\model\Order;
  37. use app\common\model\OrderGoods;
  38. use app\common\model\OrderLog;
  39. use app\common\model\SelffetchShop;
  40. use app\common\model\User;
  41. use app\common\model\UserAddress;
  42. use app\common\service\ConfigService;
  43. use app\common\service\FileService;
  44. use app\shopapi\logic\Order\FreightLogic;
  45. use app\shopapi\logic\Order\OrderLogic;
  46. use think\Exception;
  47. use think\facade\Db;
  48. use think\facade\Validate;
  49. /**
  50. * 砍价逻辑层
  51. * Class BargainLogic
  52. * @package app\shopapi\logic
  53. */
  54. class BargainLogic extends BaseLogic
  55. {
  56. /**
  57. * @notes 查看砍价商品详情
  58. * @param $params
  59. * @author Tab
  60. * @date 2021/8/28 16:13
  61. */
  62. public static function detail($params)
  63. {
  64. // 商品信息
  65. $field = [
  66. 'id' => 'goods_id',
  67. 'name' => 'goods_name',
  68. 'image',
  69. 'total_stock',
  70. 'max_price' => 'goods_max_price',
  71. 'unit_id',
  72. ];
  73. $goods = Goods::field($field)->findOrEmpty($params['goods_id']);
  74. $goods['stock_show'] = (int) ConfigService::get('goods_set', 'is_show', 1);
  75. // 获取规格项-规格值-组合
  76. $goods['spec_value_list'] = self::specValueList($params, $goods);
  77. // 获取规格项-规格值
  78. $goods['spec_value'] = self::specValue($params);
  79. // 商品图片重命名
  80. $goods['goods_image'] = $goods['image'];
  81. // 增加活动商品浏览量
  82. $where = [
  83. 'activity_id' => $params['activity_id'],
  84. 'goods_id' => $params['goods_id'],
  85. ];
  86. $bargainGoods = BargainGoods::where($where)->findOrEmpty()->toArray();
  87. BargainGoods::where($where)->update([
  88. 'visited' => $bargainGoods['visited'] + 1
  89. ]);
  90. // 增加活动浏览量
  91. $bargainActivity = BargainActivity::where('id', $params['activity_id'])->field([
  92. 'id', 'sn', 'name', 'start_time', 'end_time',
  93. 'is_distribution', 'buy_condition', 'valid_period', 'help_num',
  94. 'knife_amount_type', 'self', 'count', 'buy_limit', 'order_limit',
  95. 'use_coupon', 'status', 'visited', 'close_time', 'create_time',
  96. ])->findOrEmpty();
  97. $bargainActivity->visited = $bargainActivity->visited + 1;
  98. $bargainActivity->save();
  99. $goods['unit_name'] = '';
  100. if($goods['unit_id']){
  101. $goods['unit_name'] = $goods->unit->name;
  102. }
  103. $goods['activity'] = $bargainActivity->toArray();
  104. return $goods->hidden(['unit','unit_id'])->toArray();
  105. }
  106. /**
  107. * @notes 规格项-规格值
  108. * @param $params
  109. * @throws \think\db\exception\DataNotFoundException
  110. * @throws \think\db\exception\DbException
  111. * @throws \think\db\exception\ModelNotFoundException
  112. * @author Tab
  113. * @date 2021/9/30 10:01
  114. */
  115. public static function specValue($params)
  116. {
  117. $specList = GoodsSpec::where('goods_id', $params['goods_id'])->select()->toArray();
  118. $specValueLists = GoodsSpecValue::where('goods_id', $params['goods_id'])
  119. ->select()
  120. ->toArray();
  121. $specValue = [];
  122. foreach ($specList as $spec) {
  123. foreach ($specValueLists as $item) {
  124. if ($item['spec_id'] == $spec['id']) {
  125. $spec['spec_list'][] = $item;
  126. }
  127. }
  128. $specValue[] = $spec;
  129. }
  130. return $specValue;
  131. }
  132. /**
  133. * @notes 规格项-规格值-组合
  134. * @param $params
  135. * @author Tab
  136. * @date 2021/9/30 10:13
  137. */
  138. public static function specValueList($params, &$goods)
  139. {
  140. $field = [
  141. 'gi.*',
  142. 'bg.activity_id',
  143. 'bg.first_knife',
  144. 'bg.floor_price',
  145. ];
  146. $lists = BargainGoods::alias('bg')
  147. ->leftJoin('goods_item gi', 'gi.id = bg.item_id')
  148. ->field($field)
  149. ->where([
  150. 'bg.activity_id' => $params['activity_id'],
  151. 'bg.goods_id' => $params['goods_id'],
  152. ])
  153. ->select()
  154. ->toArray();
  155. foreach ($lists as &$item) {
  156. if (!isset($goods['min_floor_price'])) {
  157. $goods['min_price'] = $item['floor_price'];
  158. } else {
  159. $goods['min_price'] = min($goods['min_floor_price'], $item['floor_price']);
  160. }
  161. $item['image'] = empty($item['image']) ? $goods['image'] : FileService::getFileUrl($item['image']);
  162. }
  163. return $lists;
  164. }
  165. /**
  166. * @notes 发起砍价
  167. * @param $params
  168. * @return false
  169. * @author Tab
  170. * @date 2021/8/28 16:56
  171. */
  172. public static function initiate($params)
  173. {
  174. Db::startTrans();
  175. try {
  176. // 校验是否允许发起砍价活动
  177. self::checkInitiate($params);
  178. // 发起砍价
  179. $result = self::addInitiate($params);
  180. Db::commit();
  181. return $result;
  182. } catch (\Exception $e) {
  183. Db::rollback();
  184. self::setError($e->getMessage());
  185. return false;
  186. }
  187. }
  188. /**
  189. * @notes 校验是否允许发起砍价
  190. * @param $params
  191. * @throws \Exception
  192. * @author Tab
  193. * @date 2021/8/28 16:53
  194. */
  195. public static function checkInitiate($params)
  196. {
  197. $bargainActivity = BargainActivity::findOrEmpty($params['activity_id'])->toArray();
  198. if (empty($bargainActivity)) {
  199. throw new \Exception('砍价活动不存在');
  200. }
  201. if (strtotime($bargainActivity['start_time']) > time()) {
  202. throw new \Exception('砍价活动未开始');
  203. }
  204. if (strtotime($bargainActivity['end_time']) <= time()) {
  205. throw new \Exception('砍价活动已结束');
  206. }
  207. $count = BargainInitiate::where([
  208. 'activity_id' => $params['activity_id'],
  209. 'user_id' => $params['user_id'],
  210. ])->count();
  211. if ($count >= $bargainActivity['count']) {
  212. throw new \Exception('发起砍价次数超出限制');
  213. }
  214. // if ($bargainActivity['buy_limit'] && $params['goods_num'] < $bargainActivity['buy_limit']) {
  215. // throw new \Exception('购买数量低于起购数量');
  216. // }
  217. if ($bargainActivity['order_limit'] && $params['goods_num'] > $bargainActivity['order_limit']) {
  218. throw new \Exception('购买数量超出每单限制');
  219. }
  220. }
  221. /**
  222. * @notes 添加砍价记录
  223. * @param $params
  224. * @author Tab
  225. * @date 2021/8/28 17:27
  226. */
  227. public static function addInitiate($params)
  228. {
  229. // 砍价活动信息
  230. $bargainActivity = BargainActivity::findOrEmpty($params['activity_id'])->toArray();
  231. // 砍价商品信息
  232. $field = [
  233. 'bg.goods_id',
  234. 'bg.item_id',
  235. 'bg.first_knife',
  236. 'bg.floor_price',
  237. 'g.name' => 'goods_name',
  238. 'g.image' => 'goods_image',
  239. 'gi.image' => 'item_image',
  240. 'gi.spec_value_str' => 'item_spec_value_str',
  241. 'gi.sell_price' => 'item_sell_price',
  242. ];
  243. $bargainGoods = BargainGoods::alias('bg')
  244. ->leftJoin('goods g', 'g.id = bg.goods_id')
  245. ->leftJoin('goods_item gi', 'gi.id = bg.item_id')
  246. ->field($field)
  247. ->where([
  248. 'bg.activity_id' => $params['activity_id'],
  249. 'bg.item_id' => $params['item_id'],
  250. ])
  251. ->findOrEmpty()
  252. ->toArray();
  253. $bargainGoods['goods_num'] = $params['goods_num'];
  254. $data = [
  255. 'sn' => generate_sn(new BargainInitiate(), 'sn'),
  256. 'activity_id' => $params['activity_id'],
  257. 'user_id' => $params['user_id'],
  258. 'bargain_snapshot' => $bargainActivity,
  259. 'goods_snapshot' => $bargainGoods,
  260. 'help_num' => 0,
  261. 'current_price' => $bargainGoods['item_sell_price'],
  262. 'floor_price' => $bargainGoods['floor_price'],
  263. 'first_knife' => $bargainGoods['first_knife'],
  264. 'start_time' => time(),
  265. 'end_time' => time() + 60 * $bargainActivity['valid_period'],
  266. 'status' => BargainEnum::STATUS_ING,
  267. ];
  268. // 添加砍价记录
  269. $bargainInitiate = BargainInitiate::create($data);
  270. // 判断是否允许自己参与砍价
  271. if ($bargainActivity['self']) {
  272. // 校验是否允许发起帮砍
  273. self::checkHelp($bargainInitiate->id, $params['user_id']);
  274. // 生成帮助砍价记录
  275. $result = self::addHelp($bargainInitiate->id, $params['user_id']);
  276. // 返回帮砍信息
  277. return $result;
  278. }
  279. // 不能自己帮自己砍价
  280. return ['initiate_id' => $bargainInitiate->id];
  281. }
  282. /**
  283. * @notes 帮助砍价
  284. * @param $initiateId
  285. * @param $userId
  286. * @throws \Exception
  287. * @author Tab
  288. * @date 2021/8/28 17:41
  289. */
  290. public static function help($params)
  291. {
  292. Db::startTrans();
  293. try {
  294. // 校验是否允许发起帮砍
  295. self::checkHelp($params['initiate_id'], $params['user_id']);
  296. // 生成帮助砍价记录
  297. $result = self::addHelp($params['initiate_id'], $params['user_id']);
  298. Db::commit();
  299. return $result;
  300. } catch (\Exception $e) {
  301. Db::rollback();
  302. self::setError($e->getMessage());
  303. return false;
  304. }
  305. }
  306. /**
  307. * @notes 校验是否允许发起帮砍
  308. * @param $params
  309. * @throws \Exception
  310. * @author Tab
  311. * @date 2021/8/28 17:34
  312. */
  313. public static function checkHelp($initiateId, $userId)
  314. {
  315. $bargainInitiate = BargainInitiate::findOrEmpty($initiateId)->toArray();
  316. if (empty($bargainInitiate)) {
  317. throw new \Exception('砍价发起记录不存在');
  318. }
  319. $bargainActivity = BargainActivity::findOrEmpty($bargainInitiate['activity_id'])->toArray();
  320. if (empty($bargainActivity)) {
  321. throw new \Exception('砍价活动不存在');
  322. }
  323. if (strtotime($bargainActivity['start_time']) > time()) {
  324. throw new \Exception('砍价活动未开始');
  325. }
  326. if (strtotime($bargainActivity['end_time']) <= time()) {
  327. throw new \Exception('砍价活动已结束');
  328. }
  329. if (!$bargainActivity['self'] && $userId == $bargainInitiate['user_id']) {
  330. throw new \Exception('不能自己给自己砍价');
  331. }
  332. if ($bargainInitiate['status'] != BargainEnum::STATUS_ING) {
  333. throw new \Exception('非砍价中状态不允许助力');
  334. }
  335. if (($bargainInitiate['start_time'] + $bargainActivity['valid_period'] * 60) <= time()) {
  336. throw new \Exception('已过砍价有效期');
  337. }
  338. $count = BargainHelp::where([
  339. 'initiate_id' => $initiateId,
  340. 'user_id' => $userId
  341. ])->count();
  342. if ($count > 0) {
  343. throw new \Exception('不能重复帮助砍价哦');
  344. }
  345. }
  346. /**
  347. * @notes 添加帮助砍价记录
  348. * @param $initiateId
  349. * @param $userId
  350. * @author Tab
  351. * @date 2021/8/28 18:07
  352. */
  353. public static function addHelp($initiateId, $userId)
  354. {
  355. // 计算帮砍信息
  356. $calcHelp = self::calcHelp($initiateId);
  357. $data = [
  358. 'initiate_id' => $initiateId,
  359. 'user_id' => $userId,
  360. 'reduce_amount' => $calcHelp['reduce_amount'],
  361. ];
  362. // 添加帮砍记录
  363. BargainHelp::create($data);
  364. // 更新砍价记录
  365. $bargainInitiate = BargainInitiate::findOrEmpty($initiateId);
  366. $bargainInitiate->help_num = $bargainInitiate->help_num + 1;
  367. $bargainInitiate->current_price = $calcHelp['current_price'];
  368. if ($calcHelp['current_price'] <= $bargainInitiate['floor_price']) {
  369. // 砍价成功
  370. $bargainInitiate->status = BargainEnum::STATUS_SUCCESS;
  371. $bargainInitiate->end_time = time();
  372. }
  373. $bargainInitiate->save();
  374. // 返回帮砍信息
  375. return $calcHelp;
  376. }
  377. /**
  378. * @notes 计算帮砍信息
  379. * @param $initiateId
  380. * @return int[]
  381. * @author Tab
  382. * @date 2021/8/28 18:09
  383. */
  384. public static function calcHelp($initiateId)
  385. {
  386. $bargainInitiate = BargainInitiate::findOrEmpty($initiateId)->toArray();
  387. $bargainActivity = BargainActivity::findOrEmpty($bargainInitiate['activity_id'])->toArray();
  388. // 判断是否为首刀
  389. $count = BargainHelp::where('initiate_id', $bargainInitiate['id'])->count();
  390. if ($count == 0) {
  391. // 首刀
  392. $reduceAmount = $bargainInitiate['first_knife'];
  393. } else {
  394. // 非首刀
  395. $reduceAmount = self::calcReduceAmount($bargainInitiate, $bargainActivity);
  396. }
  397. // 计算帮砍后价格
  398. $currentPrice = round($bargainInitiate['current_price'] - $reduceAmount, 2);
  399. // 距离底价还差多少金额
  400. $diffPrice = round($currentPrice - $bargainInitiate['floor_price'], 2);
  401. // 一共需要砍的价格
  402. $needHelp = round($bargainInitiate['goods_snapshot']['item_sell_price'] - $bargainInitiate['floor_price'], 2);
  403. // 特殊处理: 一刀砍到低于底价的情况(例如:首刀金额设置过高的情况)
  404. if ($currentPrice < $bargainInitiate['floor_price']) {
  405. // 将帮砍后的价格设置成与底价一样
  406. $currentPrice = $bargainInitiate['floor_price'];
  407. // 将帮砍价格设置为当前价格 - 底价价格
  408. $reduceAmount = round($bargainInitiate['current_price'] - $bargainInitiate['floor_price'], 2);
  409. $diffPrice = 0;
  410. }
  411. return [
  412. 'reduce_amount' => doubleval($reduceAmount),
  413. 'current_price' => $currentPrice,
  414. 'diff_price' => $diffPrice,
  415. 'floor_price' => $bargainInitiate['floor_price'],
  416. 'need_help' => $needHelp,
  417. 'initiate_id' => intval($initiateId)
  418. ];
  419. }
  420. /**
  421. * @notes 计算当前帮砍金额
  422. * @param $bargainInitiate
  423. * @param $bargainActivity
  424. * @return float
  425. * @author Tab
  426. * @date 2021/8/28 19:01
  427. */
  428. public static function calcReduceAmount($bargainInitiate, $bargainActivity)
  429. {
  430. // 剩余刀数 = 帮砍次数 - 已砍次数
  431. $knifeCount = $bargainActivity['help_num'] - $bargainInitiate['help_num'];
  432. // 距离底价还差多少金额 = 当前价格 - 底价
  433. $leftAmount = $bargainInitiate['current_price'] - $bargainInitiate['floor_price'];
  434. // 每刀固定金额 = 距离底价还差多少金额 / 剩余刀数
  435. if ($bargainActivity['knife_amount_type'] == BargainEnum::KNIFE_TYPE_FIXED) {
  436. $reduceAmount = round($leftAmount / $knifeCount, 2);
  437. return $reduceAmount;
  438. }
  439. // 随机金额 && 非最后一刀
  440. if ($bargainActivity['knife_amount_type'] == BargainEnum::KNIFE_TYPE_RAND && $knifeCount != 1) {
  441. $leftAmount = round($leftAmount / 2, 2);
  442. $rand = mt_rand(0, $leftAmount * 100);
  443. $reduceAmount = round($rand / 100, 2);
  444. return $reduceAmount;
  445. }
  446. // 随机金额 && 最后一刀
  447. if ($bargainActivity['knife_amount_type'] == BargainEnum::KNIFE_TYPE_RAND && $knifeCount == 1) {
  448. $reduceAmount = $leftAmount;
  449. return $reduceAmount;
  450. }
  451. }
  452. /**
  453. * @notes 查看砍价进度
  454. * @param $params
  455. * @return array
  456. * @author Tab
  457. * @date 2021/9/18 16:52
  458. */
  459. public static function bargainProgress($params)
  460. {
  461. $bargainInitiate = BargainInitiate::field('*,status as status_desc')->findOrEmpty($params['initiate_id'])->toArray();
  462. $bargainInitiate['goods_image'] = get_image([
  463. $bargainInitiate['goods_snapshot']['item_image'], $bargainInitiate['goods_snapshot']['goods_image']
  464. ]);
  465. $bargainInitiate['goods_id'] = $bargainInitiate['goods_snapshot']['goods_id'];
  466. $bargainInitiate['goods_name'] = $bargainInitiate['goods_snapshot']['goods_name'];
  467. $bargainInitiate['item_spec_value_str'] = $bargainInitiate['goods_snapshot']['item_spec_value_str'];
  468. $bargainInitiate['item_sell_price'] = $bargainInitiate['goods_snapshot']['item_sell_price'];
  469. $bargainInitiate['help_record'] = self::helpRecord($bargainInitiate);
  470. $bargainInitiate['help_total'] = round($bargainInitiate['item_sell_price'] - $bargainInitiate['current_price'], 2);
  471. $bargainInitiate['help_diff'] = round($bargainInitiate['current_price'] - $bargainInitiate['floor_price'], 2);
  472. $bargainInitiate['btns'] = BargainInitiate::getBtns($bargainInitiate);
  473. unset($bargainInitiate['goods_snapshot']);
  474. unset($bargainInitiate['bargain_snapshot']);
  475. unset($bargainInitiate['create_time']);
  476. unset($bargainInitiate['update_time']);
  477. unset($bargainInitiate['delete_time']);
  478. return $bargainInitiate;
  479. }
  480. /**
  481. * @notes 帮砍信息
  482. * @param $bargainInitiate
  483. * @return array
  484. * @author Tab
  485. * @date 2021/9/18 16:25
  486. */
  487. public static function helpRecord($bargainInitiate)
  488. {
  489. $field = [
  490. 'u.nickname',
  491. 'u.avatar',
  492. 'bh.reduce_amount',
  493. ];
  494. $lists = BargainHelp::alias('bh')
  495. ->leftJoin('user u', 'u.id = bh.user_id')
  496. ->field($field)
  497. ->withAttr('avatar', function ($value) {
  498. return empty($value) ? '' : FileService::getFileUrl(trim($value, '/'));
  499. })
  500. ->where('bh.initiate_id', $bargainInitiate['id'])
  501. ->order('bh.id', 'desc')
  502. ->select()
  503. ->toArray();
  504. return $lists;
  505. }
  506. /**
  507. * @notes 分享帮砍详情
  508. * @param $params
  509. * @return array
  510. * @author Tab
  511. * @date 2021/9/18 17:47
  512. */
  513. public static function shareDetail($params)
  514. {
  515. $field = [
  516. 'id',
  517. 'activity_id',
  518. 'user_id',
  519. 'goods_snapshot',
  520. 'end_time',
  521. ];
  522. $bargainInitiate = BargainInitiate::field($field)->findOrEmpty($params['initiate_id'])->toArray();
  523. $bargainInitiate['initiate_user'] = User::field('avatar,nickname')->findOrEmpty($bargainInitiate['user_id'])->toArray();
  524. $bargainInitiate['goods_image'] = get_image([
  525. $bargainInitiate['goods_snapshot']['item_image'], $bargainInitiate['goods_snapshot']['goods_image']
  526. ]);
  527. $bargainInitiate['goods_id'] = $bargainInitiate['goods_snapshot']['goods_id'];
  528. $bargainInitiate['goods_name'] = $bargainInitiate['goods_snapshot']['goods_name'];
  529. $bargainInitiate['item_spec_value_str'] = $bargainInitiate['goods_snapshot']['item_spec_value_str'];
  530. $bargainInitiate['item_sell_price'] = $bargainInitiate['goods_snapshot']['item_sell_price'];
  531. $bargainInitiate['help_record'] = self::helpRecord($bargainInitiate);
  532. $bargainInitiate['btns'] = BargainInitiate::getBtns2($bargainInitiate, $params['user_id']);
  533. $bargainInitiate['tips'] = $bargainInitiate['end_time'] <= time() ? '砍价已结束,去看看其他商品吧~' : '';
  534. unset($bargainInitiate['goods_snapshot']);
  535. return $bargainInitiate;
  536. }
  537. }