GroupBuyController.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <?php
  2. namespace mall\controllers;
  3. use bizHd\goods\classes\GoodsClass;
  4. use bizHd\groupBuy\classes\GroupBuyActivityClass;
  5. use bizHd\groupBuy\classes\GroupBuyClass;
  6. use bizHd\groupBuy\classes\GroupBuyMemberClass;
  7. use bizHd\order\services\OrderService;
  8. use bizHd\shop\classes\ShopClass;
  9. use common\components\business;
  10. use common\components\dict;
  11. use common\components\orderSn;
  12. use common\components\stringUtil;
  13. use common\components\util;
  14. use mall\models\groupBuy\CreateGroupBuyForm;
  15. use mall\models\groupBuy\JoinGroupBuyForm;
  16. use Yii;
  17. /**
  18. * 商城端拼团接口
  19. * 落地页 / 团详情 / 开团 / 参团 / 我的拼团
  20. */
  21. class GroupBuyController extends BaseController
  22. {
  23. public $guestAccess = [
  24. 'goods-landing',
  25. 'detail',
  26. ];
  27. /**
  28. * 团购商品落地页:活动商品信息 + 当前最快成团开放团
  29. * GET goodsId
  30. */
  31. public function actionGoodsLanding()
  32. {
  33. $mainId = intval($this->mainId);
  34. if ($mainId <= 0) {
  35. util::fail('无效门店');
  36. }
  37. $goodsId = intval(Yii::$app->request->get('goodsId', 0));
  38. if ($goodsId <= 0) {
  39. util::fail('请选择商品');
  40. }
  41. $row = GroupBuyActivityClass::getActiveGoodsRowByGoodsId($mainId, $goodsId);
  42. if (empty($row)) {
  43. util::fail('团购活动已结束或商品已下架');
  44. }
  45. $goods = GoodsClass::getById($goodsId, true);
  46. $cover = strval($row['cover'] ?? '');
  47. $coverUrl = $cover;
  48. if ($cover !== '' && strpos($cover, 'http') !== 0) {
  49. $formatted = business::formatUploadImg($cover);
  50. $coverUrl = $formatted['url'] ?? $cover;
  51. }
  52. $detailImages = [];
  53. if (!empty($goods)) {
  54. $pics = $goods->detail ?? ($goods->pic ?? '');
  55. if (is_string($pics) && $pics !== '') {
  56. $decoded = json_decode($pics, true);
  57. if (is_array($decoded)) {
  58. foreach ($decoded as $p) {
  59. if (is_string($p) && $p !== '') {
  60. if (strpos($p, 'http') === 0) {
  61. $detailImages[] = $p;
  62. } else {
  63. $fmt = business::formatUploadImg($p);
  64. $detailImages[] = $fmt['url'] ?? $p;
  65. }
  66. }
  67. }
  68. }
  69. }
  70. }
  71. $openGroup = GroupBuyClass::getHottestOpenGroup(
  72. $mainId,
  73. $goodsId,
  74. intval($row['activityGoodsId'] ?? 0)
  75. );
  76. util::success([
  77. 'activityGoodsId' => intval($row['activityGoodsId']),
  78. 'activityId' => intval($row['activityId']),
  79. 'goodsId' => $goodsId,
  80. 'name' => strval($row['name']),
  81. 'cover' => $cover,
  82. 'coverUrl' => $coverUrl,
  83. 'price' => floatval($row['price']),
  84. 'originPrice' => floatval($row['originPrice']),
  85. 'groupSize' => intval($row['groupSize']),
  86. 'stock' => intval($row['stock']),
  87. 'limit' => intval($row['limit']),
  88. 'virtualGroup' => intval($row['virtualGroup']),
  89. 'virtualMinutes' => intval($row['virtualMinutes']),
  90. 'autoRefund' => 1,
  91. 'startTime' => intval($row['startTime']),
  92. 'endTime' => intval($row['endTime']),
  93. 'title' => strval($row['title']),
  94. 'subtitle' => strval($row['subtitle']),
  95. 'desc' => strval($row['desc']),
  96. 'showCountdown' => intval($row['showCountdown']),
  97. 'detailImages' => $detailImages,
  98. 'openGroup' => $openGroup,
  99. ]);
  100. }
  101. /**
  102. * 拼团详情
  103. * GET id
  104. */
  105. public function actionDetail()
  106. {
  107. $mainId = intval($this->mainId);
  108. if ($mainId <= 0) {
  109. util::fail('无效门店');
  110. }
  111. $id = intval(Yii::$app->request->get('id', 0));
  112. if ($id <= 0) {
  113. util::fail('请选择拼团');
  114. }
  115. $detail = GroupBuyClass::getDetail($mainId, $id);
  116. // 附带活动商品落地信息,便于分享进入后完整展示
  117. $landing = GroupBuyActivityClass::getActiveGoodsRowById($mainId, intval($detail['activityGoodsId']));
  118. if (empty($landing)) {
  119. $landing = GroupBuyActivityClass::getActiveGoodsRowByGoodsId($mainId, intval($detail['goodsId']));
  120. }
  121. $detail['activityGoods'] = $landing;
  122. util::success($detail);
  123. }
  124. /**
  125. * 我要开团:创建团 + 创建待支付订单 + 挂接团长成员
  126. */
  127. public function actionCreate()
  128. {
  129. $form = new CreateGroupBuyForm();
  130. $form->loadAndValidate();
  131. $mainId = intval($this->mainId);
  132. $custom = $this->custom;
  133. if (empty($custom)) {
  134. util::fail('请先登录');
  135. }
  136. $customId = intval($custom->id);
  137. $activityGoodsId = intval($form->activityGoodsId);
  138. $goodsNum = max(1, intval($form->goodsNum));
  139. $row = GroupBuyActivityClass::getActiveGoodsRowById($mainId, $activityGoodsId);
  140. if (empty($row)) {
  141. util::fail('团购活动已结束或商品已下架');
  142. }
  143. if ($goodsNum > intval($row['limit'])) {
  144. util::fail('超出单人限购数量');
  145. }
  146. $connection = Yii::$app->db;
  147. $transaction = $connection->beginTransaction();
  148. try {
  149. $group = GroupBuyClass::createGroup($mainId, intval($this->shopId), $customId, $row);
  150. $groupBuyId = intval($group['id']);
  151. $order = $this->createGroupBuyOrder($form, $row, $groupBuyId, $goodsNum);
  152. GroupBuyMemberClass::bindOrderMember(
  153. $groupBuyId,
  154. $mainId,
  155. $customId,
  156. intval($order->id),
  157. strval($order->orderSn),
  158. GroupBuyMemberClass::ROLE_LEADER
  159. );
  160. $transaction->commit();
  161. util::success([
  162. 'groupBuyId' => $groupBuyId,
  163. 'orderSn' => $order->orderSn,
  164. 'totalPrice' => $order->actPrice,
  165. 'id' => $order->id,
  166. ]);
  167. } catch (\Exception $e) {
  168. $transaction->rollBack();
  169. Yii::error('开团失败:' . $e->getMessage(), __METHOD__);
  170. $msg = $e->getMessage();
  171. if ($msg !== '' && mb_strlen($msg) < 80 && strpos($msg, 'SQLSTATE') === false) {
  172. util::fail($msg);
  173. }
  174. util::fail('开团失败');
  175. }
  176. }
  177. /**
  178. * 我要参团:加入已有团 + 创建待支付订单
  179. */
  180. public function actionJoin()
  181. {
  182. $form = new JoinGroupBuyForm();
  183. $form->loadAndValidate();
  184. $mainId = intval($this->mainId);
  185. $custom = $this->custom;
  186. if (empty($custom)) {
  187. util::fail('请先登录');
  188. }
  189. $customId = intval($custom->id);
  190. $groupBuyId = intval($form->groupBuyId);
  191. $goodsNum = max(1, intval($form->goodsNum));
  192. $connection = Yii::$app->db;
  193. $transaction = $connection->beginTransaction();
  194. try {
  195. $group = GroupBuyClass::assertCanJoin($mainId, $groupBuyId, $customId);
  196. $row = GroupBuyActivityClass::getActiveGoodsRowById($mainId, intval($group->activityGoodsId));
  197. if (empty($row)) {
  198. // 活动结束后仍允许在截止前参团:用团上快照价
  199. $row = [
  200. 'activityId' => intval($group->activityId),
  201. 'activityGoodsId' => intval($group->activityGoodsId),
  202. 'goodsId' => intval($group->goodsId),
  203. 'price' => floatval($group->price),
  204. 'limit' => 1,
  205. 'endTime' => intval($group->deadline),
  206. 'groupSize' => intval($group->needNum),
  207. 'virtualGroup' => intval($group->virtualGroup),
  208. 'virtualMinutes' => 0,
  209. ];
  210. }
  211. if ($goodsNum > intval($row['limit'] ?? 1)) {
  212. util::fail('超出单人限购数量');
  213. }
  214. $order = $this->createGroupBuyOrder($form, $row, $groupBuyId, $goodsNum);
  215. GroupBuyMemberClass::bindOrderMember(
  216. $groupBuyId,
  217. $mainId,
  218. $customId,
  219. intval($order->id),
  220. strval($order->orderSn),
  221. GroupBuyMemberClass::ROLE_MEMBER
  222. );
  223. $transaction->commit();
  224. util::success([
  225. 'groupBuyId' => $groupBuyId,
  226. 'orderSn' => $order->orderSn,
  227. 'totalPrice' => $order->actPrice,
  228. 'id' => $order->id,
  229. ]);
  230. } catch (\Exception $e) {
  231. $transaction->rollBack();
  232. Yii::error('参团失败:' . $e->getMessage(), __METHOD__);
  233. $msg = $e->getMessage();
  234. if ($msg !== '' && mb_strlen($msg) < 80 && strpos($msg, 'SQLSTATE') === false) {
  235. util::fail($msg);
  236. }
  237. util::fail('参团失败');
  238. }
  239. }
  240. /**
  241. * 我的拼团列表
  242. */
  243. public function actionMyList()
  244. {
  245. $mainId = intval($this->mainId);
  246. $custom = $this->custom;
  247. if (empty($custom)) {
  248. util::fail('请先登录');
  249. }
  250. util::success([
  251. 'list' => GroupBuyClass::getMyList($mainId, intval($custom->id)),
  252. ]);
  253. }
  254. /**
  255. * 组装并创建拼团订单(核价以后端活动商品为准)
  256. *
  257. * @param object $form
  258. * @param array $row
  259. * @param int $groupBuyId
  260. * @param int $goodsNum
  261. * @return object
  262. */
  263. private function createGroupBuyOrder($form, $row, $groupBuyId, $goodsNum)
  264. {
  265. $shop = $this->shop;
  266. $openShop = ShopClass::getOpenShop($shop);
  267. if ($openShop == 0) {
  268. util::fail('已休店');
  269. }
  270. $mainId = intval($this->mainId);
  271. $custom = $this->custom;
  272. $hd = $this->hd;
  273. $user = $this->user;
  274. $saleGoodsId = intval($row['goodsId']);
  275. $goodsInfo = GoodsClass::getById($saleGoodsId, true);
  276. if (empty($goodsInfo) || intval($goodsInfo->mainId) !== $mainId) {
  277. util::fail('没有找到商品');
  278. }
  279. $stock = intval($goodsInfo->stock ?? 0);
  280. if ($stock < $goodsNum) {
  281. util::fail('库存不足');
  282. }
  283. $unitPrice = floatval($row['price'] ?? 0);
  284. if ($unitPrice <= 0) {
  285. util::fail('团购价格异常');
  286. }
  287. $goodsPrice = bcmul($unitPrice, $goodsNum, 2);
  288. $sendType = intval($form->sendType ?? dict::getDict('sendType', 'shopGet'));
  289. $sendCost = 0;
  290. // 跑腿配送暂不在拼团链路单独报价,统一引导自取或送货上门(运费0,与花店协商)
  291. if ($sendType == dict::getDict('sendType', 'thirdSend')) {
  292. util::fail('拼团暂不支持跑腿,请选择自取或送货上门');
  293. }
  294. if ($sendType == dict::getDict('sendType', 'carGet')) {
  295. if (empty($form->receiveUserName)) {
  296. util::fail('请填写收花人名字');
  297. }
  298. if (empty($form->receiveMobile) || !stringUtil::isMobile($form->receiveMobile)) {
  299. util::fail('请填写正确的收花人手机号');
  300. }
  301. }
  302. $post = [
  303. 'sendType' => $sendType,
  304. 'receiveUserName' => strval($form->receiveUserName ?? ''),
  305. 'receiveMobile' => strval($form->receiveMobile ?? ''),
  306. 'address' => strval($form->address ?? ''),
  307. 'floor' => strval($form->floor ?? ''),
  308. 'city' => strval($form->city ?? ''),
  309. 'long' => strval($form->long ?? ''),
  310. 'lat' => strval($form->lat ?? ''),
  311. 'showAddress' => strval($form->showAddress ?? ''),
  312. 'remark' => strval($form->remark ?? ''),
  313. 'reachDate' => !empty($form->reachDate) ? $form->reachDate : date('Y-m-d'),
  314. 'reachPeriod' => strval($form->reachPeriod ?? ''),
  315. 'sendCost' => $sendCost,
  316. 'sendDistance' => 0,
  317. 'sjId' => intval($this->sjId ?: ($shop->sjId ?? 0)),
  318. 'shopId' => $this->shopId,
  319. 'mainId' => $mainId,
  320. 'userId' => $this->userId,
  321. 'customId' => intval($custom->id),
  322. 'customName' => strval($custom->name ?? ''),
  323. 'customNamePy' => stringUtil::py(strval($custom->name ?? '')),
  324. 'hdId' => intval($hd->id ?? 0),
  325. 'hdName' => strval($hd->name ?? ''),
  326. 'bookName' => strval($user->name ?? ''),
  327. 'bookMobile' => !empty($form->bookMobile) && stringUtil::isMobile($form->bookMobile)
  328. ? $form->bookMobile
  329. : strval($user->mobile ?? ''),
  330. 'payWay' => 0,
  331. 'store' => 0,
  332. 'modifyPrice' => stringUtil::calcAdd($sendCost, $goodsPrice),
  333. 'deadline' => time() + 1800,
  334. 'orderSn' => orderSn::getOrderSn(),
  335. 'fromType' => dict::getDict('fromType', 'mall'),
  336. 'cash' => 0,
  337. 'needPrint' => dict::getDict('needPrint', 'need'),
  338. 'groupBuyId' => intval($groupBuyId),
  339. 'product' => [[
  340. 'productId' => $saleGoodsId,
  341. 'unitType' => 0,
  342. 'property' => 0,
  343. 'unitPrice' => $unitPrice,
  344. 'num' => $goodsNum,
  345. ]],
  346. ];
  347. if ($sendType != 1 && empty($post['reachPeriod'])) {
  348. util::fail('请选择配送时间');
  349. }
  350. return OrderService::createHdOrder($post, $custom, 0);
  351. }
  352. }