BargainLogic.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  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. //砍价最低价
  105. $goods['bargain_min_price'] = BargainGoods::where('activity_id', $params['activity_id'])->min('floor_price');;
  106. return $goods->hidden(['unit','unit_id'])->toArray();
  107. }
  108. /**
  109. * @notes 规格项-规格值
  110. * @param $params
  111. * @throws \think\db\exception\DataNotFoundException
  112. * @throws \think\db\exception\DbException
  113. * @throws \think\db\exception\ModelNotFoundException
  114. * @author Tab
  115. * @date 2021/9/30 10:01
  116. */
  117. public static function specValue($params)
  118. {
  119. $specList = GoodsSpec::where('goods_id', $params['goods_id'])->select()->toArray();
  120. $specValueLists = GoodsSpecValue::where('goods_id', $params['goods_id'])
  121. ->select()
  122. ->toArray();
  123. $specValue = [];
  124. foreach ($specList as $spec) {
  125. foreach ($specValueLists as $item) {
  126. if ($item['spec_id'] == $spec['id']) {
  127. $spec['spec_list'][] = $item;
  128. }
  129. }
  130. $specValue[] = $spec;
  131. }
  132. return $specValue;
  133. }
  134. /**
  135. * @notes 规格项-规格值-组合
  136. * @param $params
  137. * @author Tab
  138. * @date 2021/9/30 10:13
  139. */
  140. public static function specValueList($params, &$goods)
  141. {
  142. $field = [
  143. 'gi.*',
  144. 'bg.activity_id',
  145. 'bg.first_knife',
  146. 'bg.floor_price',
  147. ];
  148. $lists = BargainGoods::alias('bg')
  149. ->leftJoin('goods_item gi', 'gi.id = bg.item_id')
  150. ->field($field)
  151. ->where([
  152. 'bg.activity_id' => $params['activity_id'],
  153. 'bg.goods_id' => $params['goods_id'],
  154. ])
  155. ->select()
  156. ->toArray();
  157. foreach ($lists as &$item) {
  158. if (!isset($goods['min_floor_price'])) {
  159. $goods['min_price'] = $item['floor_price'];
  160. } else {
  161. $goods['min_price'] = min($goods['min_floor_price'], $item['floor_price']);
  162. }
  163. $item['image'] = empty($item['image']) ? $goods['image'] : FileService::getFileUrl($item['image']);
  164. }
  165. return $lists;
  166. }
  167. /**
  168. * @notes 发起砍价
  169. * @param $params
  170. * @return false
  171. * @author Tab
  172. * @date 2021/8/28 16:56
  173. */
  174. public static function initiate($params)
  175. {
  176. Db::startTrans();
  177. try {
  178. // 校验是否允许发起砍价活动
  179. self::checkInitiate($params);
  180. // 发起砍价
  181. $result = self::addInitiate($params);
  182. Db::commit();
  183. return $result;
  184. } catch (\Exception $e) {
  185. Db::rollback();
  186. self::setError($e->getMessage());
  187. return false;
  188. }
  189. }
  190. /**
  191. * @notes 校验是否允许发起砍价
  192. * @param $params
  193. * @throws \Exception
  194. * @author Tab
  195. * @date 2021/8/28 16:53
  196. */
  197. public static function checkInitiate($params)
  198. {
  199. $bargainActivity = BargainActivity::findOrEmpty($params['activity_id'])->toArray();
  200. if (empty($bargainActivity)) {
  201. throw new \Exception('砍价活动不存在');
  202. }
  203. if (strtotime($bargainActivity['start_time']) > time()) {
  204. throw new \Exception('砍价活动未开始');
  205. }
  206. if (strtotime($bargainActivity['end_time']) <= time()) {
  207. throw new \Exception('砍价活动已结束');
  208. }
  209. $count = BargainInitiate::where([
  210. 'activity_id' => $params['activity_id'],
  211. 'user_id' => $params['user_id'],
  212. ])->count();
  213. if ($count >= $bargainActivity['count']) {
  214. throw new \Exception('发起砍价次数超出限制');
  215. }
  216. // if ($bargainActivity['buy_limit'] && $params['goods_num'] < $bargainActivity['buy_limit']) {
  217. // throw new \Exception('购买数量低于起购数量');
  218. // }
  219. if ($bargainActivity['order_limit'] && $params['goods_num'] > $bargainActivity['order_limit']) {
  220. throw new \Exception('购买数量超出每单限制');
  221. }
  222. }
  223. /**
  224. * @notes 添加砍价记录
  225. * @param $params
  226. * @author Tab
  227. * @date 2021/8/28 17:27
  228. */
  229. public static function addInitiate($params)
  230. {
  231. // 砍价活动信息
  232. $bargainActivity = BargainActivity::findOrEmpty($params['activity_id'])->toArray();
  233. // 砍价商品信息
  234. $field = [
  235. 'bg.goods_id',
  236. 'bg.item_id',
  237. 'bg.first_knife',
  238. 'bg.floor_price',
  239. 'g.name' => 'goods_name',
  240. 'g.image' => 'goods_image',
  241. 'gi.image' => 'item_image',
  242. 'gi.spec_value_str' => 'item_spec_value_str',
  243. 'gi.sell_price' => 'item_sell_price',
  244. ];
  245. $bargainGoods = BargainGoods::alias('bg')
  246. ->leftJoin('goods g', 'g.id = bg.goods_id')
  247. ->leftJoin('goods_item gi', 'gi.id = bg.item_id')
  248. ->field($field)
  249. ->where([
  250. 'bg.activity_id' => $params['activity_id'],
  251. 'bg.item_id' => $params['item_id'],
  252. ])
  253. ->findOrEmpty()
  254. ->toArray();
  255. $bargainGoods['goods_num'] = $params['goods_num'];
  256. $data = [
  257. 'sn' => generate_sn(new BargainInitiate(), 'sn'),
  258. 'activity_id' => $params['activity_id'],
  259. 'user_id' => $params['user_id'],
  260. 'bargain_snapshot' => $bargainActivity,
  261. 'goods_snapshot' => $bargainGoods,
  262. 'help_num' => 0,
  263. 'current_price' => $bargainGoods['item_sell_price'],
  264. 'floor_price' => $bargainGoods['floor_price'],
  265. 'first_knife' => $bargainGoods['first_knife'],
  266. 'start_time' => time(),
  267. 'end_time' => time() + 60 * $bargainActivity['valid_period'],
  268. 'status' => BargainEnum::STATUS_ING,
  269. ];
  270. // 添加砍价记录
  271. $bargainInitiate = BargainInitiate::create($data);
  272. // 判断是否允许自己参与砍价
  273. if ($bargainActivity['self']) {
  274. // 校验是否允许发起帮砍
  275. self::checkHelp($bargainInitiate->id, $params['user_id']);
  276. // 生成帮助砍价记录
  277. $result = self::addHelp($bargainInitiate->id, $params['user_id']);
  278. // 返回帮砍信息
  279. return $result;
  280. }
  281. // 不能自己帮自己砍价
  282. return ['initiate_id' => $bargainInitiate->id];
  283. }
  284. /**
  285. * @notes 帮助砍价
  286. * @param $initiateId
  287. * @param $userId
  288. * @throws \Exception
  289. * @author Tab
  290. * @date 2021/8/28 17:41
  291. */
  292. public static function help($params)
  293. {
  294. Db::startTrans();
  295. try {
  296. // 校验是否允许发起帮砍
  297. self::checkHelp($params['initiate_id'], $params['user_id']);
  298. // 生成帮助砍价记录
  299. $result = self::addHelp($params['initiate_id'], $params['user_id']);
  300. Db::commit();
  301. return $result;
  302. } catch (\Exception $e) {
  303. Db::rollback();
  304. self::setError($e->getMessage());
  305. return false;
  306. }
  307. }
  308. /**
  309. * @notes 校验是否允许发起帮砍
  310. * @param $params
  311. * @throws \Exception
  312. * @author Tab
  313. * @date 2021/8/28 17:34
  314. */
  315. public static function checkHelp($initiateId, $userId)
  316. {
  317. $bargainInitiate = BargainInitiate::findOrEmpty($initiateId)->toArray();
  318. if (empty($bargainInitiate)) {
  319. throw new \Exception('砍价发起记录不存在');
  320. }
  321. $bargainActivity = BargainActivity::findOrEmpty($bargainInitiate['activity_id'])->toArray();
  322. if (empty($bargainActivity)) {
  323. throw new \Exception('砍价活动不存在');
  324. }
  325. if (strtotime($bargainActivity['start_time']) > time()) {
  326. throw new \Exception('砍价活动未开始');
  327. }
  328. if (strtotime($bargainActivity['end_time']) <= time()) {
  329. throw new \Exception('砍价活动已结束');
  330. }
  331. if (!$bargainActivity['self'] && $userId == $bargainInitiate['user_id']) {
  332. throw new \Exception('不能自己给自己砍价');
  333. }
  334. if ($bargainInitiate['status'] != BargainEnum::STATUS_ING) {
  335. throw new \Exception('非砍价中状态不允许助力');
  336. }
  337. if (($bargainInitiate['start_time'] + $bargainActivity['valid_period'] * 60) <= time()) {
  338. throw new \Exception('已过砍价有效期');
  339. }
  340. $count = BargainHelp::where([
  341. 'initiate_id' => $initiateId,
  342. 'user_id' => $userId
  343. ])->count();
  344. if ($count > 0) {
  345. throw new \Exception('不能重复帮助砍价哦');
  346. }
  347. }
  348. /**
  349. * @notes 添加帮助砍价记录
  350. * @param $initiateId
  351. * @param $userId
  352. * @author Tab
  353. * @date 2021/8/28 18:07
  354. */
  355. public static function addHelp($initiateId, $userId)
  356. {
  357. // 计算帮砍信息
  358. $calcHelp = self::calcHelp($initiateId);
  359. $data = [
  360. 'initiate_id' => $initiateId,
  361. 'user_id' => $userId,
  362. 'reduce_amount' => $calcHelp['reduce_amount'],
  363. ];
  364. // 添加帮砍记录
  365. BargainHelp::create($data);
  366. // 更新砍价记录
  367. $bargainInitiate = BargainInitiate::findOrEmpty($initiateId);
  368. $bargainInitiate->help_num = $bargainInitiate->help_num + 1;
  369. $bargainInitiate->current_price = $calcHelp['current_price'];
  370. if ($calcHelp['current_price'] <= $bargainInitiate['floor_price']) {
  371. // 砍价成功
  372. $bargainInitiate->status = BargainEnum::STATUS_SUCCESS;
  373. $bargainInitiate->end_time = time();
  374. }
  375. $bargainInitiate->save();
  376. // 返回帮砍信息
  377. return $calcHelp;
  378. }
  379. /**
  380. * @notes 计算帮砍信息
  381. * @param $initiateId
  382. * @return int[]
  383. * @author Tab
  384. * @date 2021/8/28 18:09
  385. */
  386. public static function calcHelp($initiateId)
  387. {
  388. $bargainInitiate = BargainInitiate::findOrEmpty($initiateId)->toArray();
  389. $bargainActivity = BargainActivity::findOrEmpty($bargainInitiate['activity_id'])->toArray();
  390. // 判断是否为首刀
  391. $count = BargainHelp::where('initiate_id', $bargainInitiate['id'])->count();
  392. if ($count == 0) {
  393. // 首刀
  394. $reduceAmount = $bargainInitiate['first_knife'];
  395. } else {
  396. // 非首刀
  397. $reduceAmount = self::calcReduceAmount($bargainInitiate, $bargainActivity);
  398. }
  399. // 计算帮砍后价格
  400. $currentPrice = round($bargainInitiate['current_price'] - $reduceAmount, 2);
  401. // 距离底价还差多少金额
  402. $diffPrice = round($currentPrice - $bargainInitiate['floor_price'], 2);
  403. // 一共需要砍的价格
  404. $needHelp = round($bargainInitiate['goods_snapshot']['item_sell_price'] - $bargainInitiate['floor_price'], 2);
  405. // 特殊处理: 一刀砍到低于底价的情况(例如:首刀金额设置过高的情况)
  406. if ($currentPrice < $bargainInitiate['floor_price']) {
  407. // 将帮砍后的价格设置成与底价一样
  408. $currentPrice = $bargainInitiate['floor_price'];
  409. // 将帮砍价格设置为当前价格 - 底价价格
  410. $reduceAmount = round($bargainInitiate['current_price'] - $bargainInitiate['floor_price'], 2);
  411. $diffPrice = 0;
  412. }
  413. return [
  414. 'reduce_amount' => doubleval($reduceAmount),
  415. 'current_price' => $currentPrice,
  416. 'diff_price' => $diffPrice,
  417. 'floor_price' => $bargainInitiate['floor_price'],
  418. 'need_help' => $needHelp,
  419. 'initiate_id' => intval($initiateId)
  420. ];
  421. }
  422. /**
  423. * @notes 计算当前帮砍金额
  424. * @param $bargainInitiate
  425. * @param $bargainActivity
  426. * @return float
  427. * @author Tab
  428. * @date 2021/8/28 19:01
  429. */
  430. public static function calcReduceAmount($bargainInitiate, $bargainActivity)
  431. {
  432. // 剩余刀数 = 帮砍次数 - 已砍次数
  433. $knifeCount = $bargainActivity['help_num'] - $bargainInitiate['help_num'];
  434. // 距离底价还差多少金额 = 当前价格 - 底价
  435. $leftAmount = $bargainInitiate['current_price'] - $bargainInitiate['floor_price'];
  436. // 每刀固定金额 = 距离底价还差多少金额 / 剩余刀数
  437. if ($bargainActivity['knife_amount_type'] == BargainEnum::KNIFE_TYPE_FIXED) {
  438. $reduceAmount = round($leftAmount / $knifeCount, 2);
  439. return $reduceAmount;
  440. }
  441. // 随机金额 && 非最后一刀
  442. if ($bargainActivity['knife_amount_type'] == BargainEnum::KNIFE_TYPE_RAND && $knifeCount != 1) {
  443. $leftAmount = round($leftAmount / 2, 2);
  444. $rand = mt_rand(0, $leftAmount * 100);
  445. $reduceAmount = round($rand / 100, 2);
  446. return $reduceAmount;
  447. }
  448. // 随机金额 && 最后一刀
  449. if ($bargainActivity['knife_amount_type'] == BargainEnum::KNIFE_TYPE_RAND && $knifeCount == 1) {
  450. $reduceAmount = $leftAmount;
  451. return $reduceAmount;
  452. }
  453. }
  454. /**
  455. * @notes 查看砍价进度
  456. * @param $params
  457. * @return array
  458. * @author Tab
  459. * @date 2021/9/18 16:52
  460. */
  461. public static function bargainProgress($params)
  462. {
  463. $bargainInitiate = BargainInitiate::field('*,status as status_desc')->findOrEmpty($params['initiate_id'])->toArray();
  464. $bargainInitiate['goods_image'] = get_image([
  465. $bargainInitiate['goods_snapshot']['item_image'], $bargainInitiate['goods_snapshot']['goods_image']
  466. ]);
  467. $bargainInitiate['goods_id'] = $bargainInitiate['goods_snapshot']['goods_id'];
  468. $bargainInitiate['goods_name'] = $bargainInitiate['goods_snapshot']['goods_name'];
  469. $bargainInitiate['item_spec_value_str'] = $bargainInitiate['goods_snapshot']['item_spec_value_str'];
  470. $bargainInitiate['item_sell_price'] = $bargainInitiate['goods_snapshot']['item_sell_price'];
  471. $bargainInitiate['help_record'] = self::helpRecord($bargainInitiate);
  472. $bargainInitiate['help_total'] = round($bargainInitiate['item_sell_price'] - $bargainInitiate['current_price'], 2);
  473. $bargainInitiate['help_diff'] = round($bargainInitiate['current_price'] - $bargainInitiate['floor_price'], 2);
  474. $bargainInitiate['btns'] = BargainInitiate::getBtns($bargainInitiate);
  475. unset($bargainInitiate['goods_snapshot']);
  476. unset($bargainInitiate['bargain_snapshot']);
  477. unset($bargainInitiate['create_time']);
  478. unset($bargainInitiate['update_time']);
  479. unset($bargainInitiate['delete_time']);
  480. return $bargainInitiate;
  481. }
  482. /**
  483. * @notes 帮砍信息
  484. * @param $bargainInitiate
  485. * @return array
  486. * @author Tab
  487. * @date 2021/9/18 16:25
  488. */
  489. public static function helpRecord($bargainInitiate)
  490. {
  491. $field = [
  492. 'u.nickname',
  493. 'u.avatar',
  494. 'bh.reduce_amount',
  495. ];
  496. $lists = BargainHelp::alias('bh')
  497. ->leftJoin('user u', 'u.id = bh.user_id')
  498. ->field($field)
  499. ->withAttr('avatar', function ($value) {
  500. return empty($value) ? '' : FileService::getFileUrl(trim($value, '/'));
  501. })
  502. ->where('bh.initiate_id', $bargainInitiate['id'])
  503. ->order('bh.id', 'desc')
  504. ->select()
  505. ->toArray();
  506. return $lists;
  507. }
  508. /**
  509. * @notes 分享帮砍详情
  510. * @param $params
  511. * @return array
  512. * @author Tab
  513. * @date 2021/9/18 17:47
  514. */
  515. public static function shareDetail($params)
  516. {
  517. $field = [
  518. 'id',
  519. 'activity_id',
  520. 'user_id',
  521. 'goods_snapshot',
  522. 'end_time',
  523. ];
  524. $bargainInitiate = BargainInitiate::field($field)->findOrEmpty($params['initiate_id'])->toArray();
  525. $bargainInitiate['initiate_user'] = User::field('avatar,nickname')->findOrEmpty($bargainInitiate['user_id'])->toArray();
  526. $bargainInitiate['goods_image'] = get_image([
  527. $bargainInitiate['goods_snapshot']['item_image'], $bargainInitiate['goods_snapshot']['goods_image']
  528. ]);
  529. $bargainInitiate['goods_id'] = $bargainInitiate['goods_snapshot']['goods_id'];
  530. $bargainInitiate['goods_name'] = $bargainInitiate['goods_snapshot']['goods_name'];
  531. $bargainInitiate['item_spec_value_str'] = $bargainInitiate['goods_snapshot']['item_spec_value_str'];
  532. $bargainInitiate['item_sell_price'] = $bargainInitiate['goods_snapshot']['item_sell_price'];
  533. $bargainInitiate['help_record'] = self::helpRecord($bargainInitiate);
  534. $bargainInitiate['btns'] = BargainInitiate::getBtns2($bargainInitiate, $params['user_id']);
  535. $bargainInitiate['tips'] = $bargainInitiate['end_time'] <= time() ? '砍价已结束,去看看其他商品吧~' : '';
  536. unset($bargainInitiate['goods_snapshot']);
  537. return $bargainInitiate;
  538. }
  539. }