CloudStockFillOrderService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <?php
  2. namespace addons\CloudStock\api\services;
  3. use addons\CloudStock\common\enums\CloudStockEnum;
  4. use addons\CloudStock\common\logic\order\PayNotify;
  5. use addons\CloudStock\common\logic\order\RestrictPurchases;
  6. use addons\CloudStock\common\models\CloudStockAgent;
  7. use addons\CloudStock\common\models\CloudStockBatchFillOrder;
  8. use addons\CloudStock\common\models\CloudStockFillOrder;
  9. use addons\CloudStock\common\models\CloudStockGoods;
  10. use addons\CloudStock\common\models\CloudStockLevel;
  11. use addons\CloudStock\common\models\CouponSetting;
  12. use addons\CloudStock\common\services\BaseService;
  13. use common\enums\AddonsEnum;
  14. use common\enums\StatusEnum;
  15. use common\helpers\FormatHelper;
  16. use common\helpers\StringHelper;
  17. use common\models\goods\Goods;
  18. use Exception;
  19. use RuntimeException;
  20. use Yii;
  21. class CloudStockFillOrderService extends BaseService
  22. {
  23. /**
  24. * 创建订单前验证
  25. *
  26. * @param int $goodsId
  27. * @param int $number
  28. *
  29. * @return array
  30. */
  31. public function createBeforeCheck(int $goodsId, int $number): array
  32. {
  33. // 当前会员是否可以补货当前商品
  34. $rr = CloudStockGoods::canFillGood($this->mall_id, $goodsId, $this->stock_agent->level, $this->user_id);
  35. if (false === $rr) {
  36. throw new RuntimeException(CloudStockGoods::getStaticError() ?: '暂不能补货当前商品');
  37. }
  38. $agent_level = $this->stock_agent->level;
  39. $level_model = CloudStockLevel::findOne(['mall_id' => $this->mall_id, 'level' => $agent_level, 'status' => StatusEnum::ENABLED]);
  40. if (!empty($level_model['is_open_fill_min']) && !empty($level_model['fill_min']) && $number < $level_model['fill_min']) {
  41. throw new RuntimeException('每次补货的最低数量为' . $level_model['fill_min']);
  42. }
  43. $all_stocks = CloudStockGoods::allPerantAgentGoodStocks($goodsId, $this->user_id, $this->mall_id);
  44. if (false === $all_stocks) {
  45. throw new RuntimeException(CloudStockGoods::getStaticError());
  46. }
  47. if ($number > $all_stocks) {
  48. throw new RuntimeException('补货数量超过了商品库存');
  49. }
  50. $stock_good = CloudStockGoods::findOne(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'goods_id' => $goodsId]);
  51. if (empty($stock_good)) {
  52. throw new RuntimeException('商品数据异常');
  53. }
  54. if (!empty($stock_good['min_replenish']) && $number < $stock_good['min_replenish']) {
  55. throw new RuntimeException('最低补货数量:' . $stock_good['min_replenish']);
  56. }
  57. if (!empty($stock_good['restrict_permissions'])) {
  58. $restrict_permissions = json_decode($stock_good['restrict_permissions'], true);
  59. if (!empty($restrict_permissions)) {
  60. $cloud_stock_agent = CloudStockAgent::find()->where(['user_id' => $this->user_id, 'status' => StatusEnum::ENABLED, 'mall_id' => $this->mall_id])->one();
  61. if (in_array($cloud_stock_agent['level'], $restrict_permissions)) {
  62. throw new RuntimeException('您当前等级禁止购买此商品');
  63. }
  64. }
  65. }
  66. try {
  67. RestrictPurchases::check($this->mall_id, $this->user_id, [$goodsId], $number);
  68. } catch (Exception $e) {
  69. throw new RuntimeException($e->getMessage());
  70. }
  71. return [
  72. 'stock_good' => $stock_good
  73. ];
  74. }
  75. /**
  76. * 购物车订单预览
  77. *
  78. * @param array $goodsNumberArr
  79. *
  80. * @return array
  81. */
  82. public function preview(array $goodsNumberArr): array
  83. {
  84. if (empty($goodsNumberArr)) {
  85. return [];
  86. }
  87. $goodsList = Goods::lists([
  88. 'select' => 'id, goods_name, cover_pic, price, stock',
  89. 'where' => [
  90. [
  91. 'mall_id' => $this->mall_id,
  92. 'status' => StatusEnum::ENABLED,
  93. 'id' => array_column($goodsNumberArr, 'goods_id'),
  94. 'is_on_sale' => 1,
  95. 'is_attr' => 0
  96. ]
  97. ],
  98. 'index' => 'id'
  99. ]);
  100. $cloudStockGoodsList = CloudStockGoods::lists([
  101. 'where' => [
  102. [
  103. 'mall_id' => $this->mall_id,
  104. 'status' => StatusEnum::ENABLED,
  105. 'goods_id' => array_column($goodsList, 'id')
  106. ]
  107. ],
  108. 'select' => 'id, goods_id, agent_price, is_allow_fill, is_take_by_range, min_replenish,is_buhuo,is_peihuo'
  109. ]);
  110. $goodsNumberArr = array_column($goodsNumberArr, null, 'goods_id');
  111. $is_buhuo_arr = [];
  112. foreach ($cloudStockGoodsList as &$item) {
  113. $item['num'] = $goodsNumberArr[$item['goods_id']]['num'];
  114. $item['goods_info'] = $goodsList[$item['goods_id']] ?? [];
  115. $item['give_text'] = '';
  116. if($item['is_buhuo'] == 1){
  117. $is_buhuo_arr[] = [
  118. 'goods_id'=>$item['goods_id'],
  119. 'num'=>$item['num'],
  120. ];
  121. }
  122. }
  123. unset($item);
  124. $goodsService = new CloudStockGoodsService(['user_id' => $this->user_id, 'mall_id' => $this->mall_id, 'stock_agent' => $this->stock_agent]);
  125. $totalAmount = $goodsService->getTotalOrderFields(
  126. $cloudStockGoodsList,
  127. $goodsList,
  128. array_column($goodsNumberArr, 'num', 'goods_id')
  129. );
  130. $configs = \Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_CLOUD_STOCK, 'basic');
  131. $give_text_arr = [];
  132. if(empty($is_buhuo_arr) == false){
  133. $coupon = CouponSetting::getGiveCoupon($this->mall_id,$is_buhuo_arr,$this->stock_agent->level);
  134. foreach ($is_buhuo_arr as $item){
  135. if(empty($coupon[$item['goods_id']]) == false){
  136. $give_text = '当前商品一次性补货每满'.$coupon[$item['goods_id']]['rule_num'].'件('.$coupon[$item['goods_id']]['rule_num'].'的整数倍),可获得';
  137. $coupon_text = [];
  138. foreach ($coupon[$item['goods_id']]['coupon_arr'] as $arr){
  139. $coupon_text[] = $arr['num'].'张“'.$arr['name'].'”';
  140. }
  141. $give_text .= implode('、',$coupon_text).'优惠券';
  142. $give_text_arr[$item['goods_id']] = $give_text;
  143. }
  144. }
  145. foreach ($cloudStockGoodsList as $key=>$item){
  146. if(isset($give_text_arr[$item['goods_id']])){
  147. $cloudStockGoodsList[$key]['give_text'] = $give_text_arr[$item['goods_id']];
  148. }
  149. }
  150. }
  151. return [
  152. 'goods_list' => $cloudStockGoodsList,
  153. 'total_amount' => $totalAmount,
  154. 'condition' => json_decode($configs['rand_condition'] ?? '', true),
  155. 'is_enable_rand_fee' => $configs['is_enable_rand_fee'] ?? 0,
  156. 'give_text' => $give_text_arr
  157. ];
  158. }
  159. /**
  160. * 过滤不能下单的商品
  161. *
  162. * @return void
  163. */
  164. public function filterGoods(array &$goodsNumberArr)
  165. {
  166. $csGoodsService = new CloudStockGoodsService(['user_id' => $this->user_id, 'mall_id' => $this->mall_id, 'stock_agent' => $this->stock_agent]);
  167. $enableGoodsList = $csGoodsService->getEnableGoodsList(array_column($goodsNumberArr, 'goods_id'));
  168. foreach ($goodsNumberArr as $key => $item) {
  169. if (!in_array($item['goods_id'], $enableGoodsList)) {
  170. unset($goodsNumberArr[$key]);
  171. }
  172. }
  173. }
  174. /**
  175. * @param array $cartList
  176. *
  177. * @return array
  178. * @throws Exception
  179. */
  180. public function batchFill(array $cartList): array
  181. {
  182. $transaction = Yii::$app->db->beginTransaction();
  183. $fillResArr = [];
  184. $order_trade_info = [];
  185. try {
  186. foreach ($cartList as $item) {
  187. $fillRes = $this->fill(['goods_id' => $item['goods_id'], 'number' => $item['num']]);
  188. // 创建支付流水
  189. $order_trade_info[] = [
  190. 'order_no' => $fillRes['order_no'],
  191. 'amount' => $fillRes['pay_money'],
  192. 'title' => '云库存补货订单',
  193. 'notify_class' => PayNotify::class,
  194. 'order_type' => AddonsEnum::ADDONS_NAME_CLOUD_STOCK,
  195. ];
  196. $fillResArr[] = $fillRes;
  197. }
  198. //
  199. $batchFillOrder = CloudStockBatchFillOrder::setData([
  200. 'mall_id' => $this->mall_id,
  201. 'user_id' => $this->user_id,
  202. 'full_order_info' => $fillResArr,
  203. ]);
  204. if ($batchFillOrder === false || empty($batchFillOrder['id'])) {
  205. throw new RuntimeException(CloudStockBatchFillOrder::getStaticError() ?: '创建补货订单失败');
  206. }
  207. $transaction->commit();
  208. } catch (Exception $e) {
  209. Yii::$app->custom->logs(FormatHelper::exception($e, '批量补货报错'), compact('cartList', 'fillResArr', 'order_trade_info'));
  210. $transaction->rollBack();
  211. throw $e;
  212. }
  213. $res_trade = Yii::$app->services->pay->createTrade($this->mall_id, $this->user_id, $order_trade_info);
  214. if ($res_trade == false) {
  215. throw new RuntimeException('生成支付流水失败');
  216. }
  217. $transaction = Yii::$app->db->beginTransaction();
  218. try {
  219. // 更新批量补货订单
  220. CloudStockBatchFillOrder::setData([
  221. 'mall_id' => $this->mall_id,
  222. 'id' => $batchFillOrder['id'],
  223. 'trade_id' => $res_trade['trade_id'],
  224. 'trade_no' => $res_trade['trade_no']
  225. ]);
  226. // 更新补货订单支付流水
  227. CloudStockFillOrder::updateAll(
  228. [
  229. 'updated_at' => time(),
  230. 'trade_id' => $res_trade['trade_id'],
  231. 'trade_no' => $res_trade['trade_no'],
  232. 'batch_fill_order_id' => $batchFillOrder['id']
  233. ],
  234. [
  235. 'mall_id' => $this->mall_id,
  236. 'status' => [StatusEnum::ENABLED, StatusEnum::DISABLED],
  237. 'id' => array_column($fillResArr, 'fill_order_id')
  238. ]
  239. );
  240. // 购物车改为已下单状态
  241. (new CloudStockFillCartService(['user_id' => $this->user_id, 'mall_id' => $this->mall_id]))->cart2Order($batchFillOrder['id']);
  242. $transaction->commit();
  243. } catch (Exception $e) {
  244. Yii::$app->custom->logs(FormatHelper::exception($e, '批量补货报错'), compact('cartList', 'fillResArr', 'order_trade_info'));
  245. $transaction->rollBack();
  246. throw $e;
  247. }
  248. // 获取设置
  249. $configs = Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_CLOUD_STOCK, 'basic');
  250. $is_enable_rand_fee = $configs['is_enable_rand_fee'] ?? 0;
  251. return [
  252. 'trade_no' => $res_trade['trade_no'],
  253. 'trade_id' => $res_trade['trade_id'],
  254. 'order_ids' => array_column($fillResArr, 'fill_order_id'),
  255. 'is_enable_rand_fee' => $is_enable_rand_fee,
  256. 'batch_order_id' => $batchFillOrder['id']
  257. ];
  258. }
  259. /**
  260. * 补货单件补货
  261. *
  262. * @param array $post
  263. *
  264. * @return array
  265. */
  266. public function fill(array $post)
  267. {
  268. $checkRes = $this->createBeforeCheck($post['goods_id'], $post['number']);
  269. if (empty($checkRes['stock_good'])) {
  270. throw new RuntimeException('提交信息异常');
  271. }
  272. $stock_good = $checkRes['stock_good'];
  273. $agent_price = json_decode($stock_good['agent_price'], true);
  274. $agent_price = array_column($agent_price, null, 'level');
  275. $unit_price = 0;
  276. if (!empty($agent_price[$this->stock_agent->level])) {
  277. $unit_price = $agent_price[$this->stock_agent->level]['price'];
  278. if (!empty($stock_good['is_take_by_range'])) {
  279. foreach ($agent_price[$this->stock_agent->level]['price_list'] as $item) {
  280. if ($item['start_num'] <= $post['number'] && $post['number'] <= $item['end_num']) {
  281. $unit_price = $item['price'];
  282. }
  283. }
  284. }
  285. }
  286. if ($unit_price <= 0) {
  287. throw new RuntimeException('商品配置异常,请稍后再试');
  288. }
  289. $pay_money = bcmul($unit_price, $post['number'], 2);
  290. $order_no = StringHelper::generateOrderNo('CSF');
  291. $params = [
  292. 'user_id' => $this->user_id,
  293. 'pay_money' => $pay_money,
  294. 'order_no' => $order_no,
  295. 'is_pay' => CloudStockEnum::FILL_STATUS_NOT_PAY,
  296. 'goods_id' => $post['goods_id'],
  297. 'num' => $post['number'],
  298. ];
  299. $res_fill_order = CloudStockFillOrder::setData($this->mall_id, $params);
  300. if (false === $res_fill_order) {
  301. throw new RuntimeException(CloudStockFillOrder::getStaticError());
  302. }
  303. return [
  304. 'fill_order_id' => $res_fill_order['id'],
  305. 'order_no' => $order_no,
  306. 'pay_money' => $pay_money,
  307. ];
  308. }
  309. /**
  310. * @param int $batchOrderId
  311. *
  312. * @return void
  313. */
  314. public function getBatchFillOrderDetails(int $batchOrderId): array
  315. {
  316. // 获取设置
  317. $configs = \Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_CLOUD_STOCK, 'basic');
  318. $batchOrder = CloudStockBatchFillOrder::getOne(
  319. [
  320. [
  321. 'mall_id' => $this->mall_id,
  322. 'user_id' => $this->user_id,
  323. 'id' => $batchOrderId,
  324. 'status' => StatusEnum::ENABLED
  325. ]
  326. ],
  327. true,
  328. [
  329. 'fillOrders' => function ($query) {
  330. $query
  331. ->select('id, batch_fill_order_id, order_no, goods_id, num, created_at')
  332. ->with([
  333. 'goods' => function ($query) {
  334. $query->select('id, cover_pic, goods_name, price');
  335. }
  336. ]);
  337. },
  338. 'paymentTrade' => function ($query) {
  339. $query->select('id, pay_fee, rand_fee, total_fee, pay_time');
  340. }
  341. ],
  342. false,
  343. 'id, trade_id'
  344. );
  345. if (empty($batchOrder)) {
  346. return [];
  347. }
  348. if (!empty($batchOrder['paymentTrade'])) {
  349. $batchOrder['paymentTrade']['pay_time_text'] = date('Y-m-d H:i:s', $batchOrder['paymentTrade']['pay_time']);
  350. }
  351. if (!empty($batchOrder['fillOrders'])) {
  352. foreach ($batchOrder['fillOrders'] as &$fillOrder) {
  353. $fillOrder['created_at_text'] = date('Y-m-d H:i:s', $fillOrder['created_at']);
  354. }
  355. }
  356. return [
  357. 'order_info' => $batchOrder,
  358. 'is_enable_rand_fee' => $configs['is_enable_rand_fee'] ?? 0
  359. ];
  360. }
  361. }