LuckyGroupOrder.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <?php
  2. namespace addons\LuckyGroup\common\models;
  3. use \addons\LuckyGroup\common\events\order\LuckyGroupOrderPaidEvent;
  4. use addons\Coupon\common\models\AddonsCouponUserRelate;
  5. use addons\LuckyGroup\common\enums\LuckyGroupEnum;
  6. use common\enums\AssetsType;
  7. use common\enums\OrderStatusEnum;
  8. use common\enums\PaymentTradeOrderEnum;
  9. use common\enums\PayTypeEnum;
  10. use common\enums\RedisKeyEnum;
  11. use common\enums\StatusEnum;
  12. use common\enums\UserWalletEnum;
  13. use common\helpers\LockHelper;
  14. use common\helpers\StringHelper;
  15. use common\models\base\BaseActiveRecord;
  16. use common\models\common\PaymentTrade;
  17. use common\models\common\PaymentTradeGroupOrder;
  18. use common\models\goods\Goods;
  19. use common\models\goods\GoodsAttr;
  20. use common\models\user\User;
  21. use common\models\user\UserAddress;
  22. use services\common\UserWalletService;
  23. use Yii;
  24. use yii\helpers\Json;
  25. /**
  26. * This is the model class for table "qimall_addons_lucky_group_order".
  27. *
  28. * @property int $id id
  29. * @property int $mall_id 商城ID
  30. * @property int $user_id 用户ID
  31. * @property string $order_no 订单编号
  32. * @property int $activity_id 拼团活动id
  33. * @property int $goods_id 商品id
  34. * @property int $goods_attr_id 商品规格id
  35. * @property int $group_status 拼团状态 0待支付 1参团中 2拼团成功 3拼团失败
  36. * @property int $attend_type 拼团方式 1手动拼团 2自动拼团
  37. * @property float $money 订单金额
  38. * @property int $buy_nums 购买数量
  39. * @property int $win_time 拼中时间
  40. * @property int $win_nums 拼中次数
  41. * @property int $payment_type 支付方式:1.在线支付 2.货到付款 3.余额支付
  42. * @property int $pay_at 支付时间
  43. * @property string $order_id 主订单id,多个订单使用逗号隔开
  44. * @property int $mch_id 多商户id,0表示商城订单
  45. * @property float $total_price 订单总金额(含运费)
  46. * @property float $total_pay_price 实际支付总费用(含运费)
  47. * @property float $shipping_money 运费
  48. * @property float $total_goods_price 订单商品总金额(优惠后)
  49. * @property float $total_goods_original_price 订单商品总金额(优惠前)
  50. * @property string $remark 用户订单备注
  51. * @property int $is_pay 是否支付:0.未支付|1.已支付
  52. * @property int $address_id 收货地址id
  53. * @property string|null $major_data 主订单数据
  54. * @property string|null $extra 额外信息
  55. * @property int $open_times 开奖次数
  56. * @property int $yu_attend_times 预扣参团次数
  57. * @property float $consume 预扣消耗
  58. * @property string $expend_type 消耗类型
  59. * @property int $status 状态[-1:删除;0:禁用;1启用]
  60. * @property int $created_at 创建时间
  61. * @property int $updated_at 更新时间
  62. */
  63. class LuckyGroupOrder extends BaseActiveRecord
  64. {
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public static function tableName()
  69. {
  70. return '{{%addons_lucky_group_order}}';
  71. }
  72. /**
  73. * {@inheritdoc}
  74. */
  75. public function rules()
  76. {
  77. return [
  78. [['mall_id', 'user_id', 'activity_id', 'goods_id', 'goods_attr_id', 'group_status', 'attend_type', 'buy_nums', 'win_time', 'win_nums', 'payment_type', 'pay_at', 'mch_id', 'is_pay', 'open_times', 'yu_attend_times', 'status', 'created_at', 'updated_at', 'address_id'], 'integer'],
  79. [['order_no'], 'required'],
  80. [['money', 'total_price', 'total_pay_price', 'shipping_money', 'total_goods_price', 'total_goods_original_price', 'consume'], 'number'],
  81. [['major_data', 'extra', 'expend_type'], 'string'],
  82. [['order_no'], 'string', 'max' => 100],
  83. [['order_id', 'remark'], 'string', 'max' => 255],
  84. ];
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public function attributeLabels()
  90. {
  91. return [
  92. 'id' => 'id',
  93. 'mall_id' => '商城ID',
  94. 'user_id' => '用户ID',
  95. 'order_no' => '订单编号',
  96. 'activity_id' => '拼团活动id',
  97. 'goods_id' => '商品id',
  98. 'goods_attr_id' => '商品规格id',
  99. 'group_status' => '拼团状态 0待支付 1参团中 2拼团成功 3拼团失败',
  100. 'attend_type' => '拼团方式 1手动拼团 2自动拼团',
  101. 'money' => '订单金额',
  102. 'buy_nums' => '购买数量',
  103. 'win_time' => '拼中时间',
  104. 'win_nums' => '拼中次数',
  105. 'payment_type' => '支付方式:1.在线支付 2.货到付款 3.余额支付',
  106. 'pay_at' => '支付时间',
  107. 'order_id' => '主订单id,多个订单使用逗号隔开',
  108. 'mch_id' => '多商户id,0表示商城订单',
  109. 'total_price' => '订单总金额(含运费)',
  110. 'total_pay_price' => '实际支付总费用(含运费)',
  111. 'shipping_money' => '运费',
  112. 'total_goods_price' => '订单商品总金额(优惠后)',
  113. 'total_goods_original_price' => '订单商品总金额(优惠前)',
  114. 'remark' => '用户订单备注',
  115. 'is_pay' => '是否支付:0.未支付|1.已支付',
  116. 'address_id' => '用户收货地址id',
  117. 'major_data' => '主订单数据',
  118. 'extra' => '额外信息',
  119. 'open_times' => '开奖次数',
  120. 'yu_attend_times' => '预扣参团次数',
  121. 'consume' => '预扣消耗',
  122. 'expend_type' => '消耗类型',
  123. 'status' => '状态[-1:删除;0:禁用;1启用]',
  124. 'created_at' => '创建时间',
  125. 'updated_at' => '更新时间',
  126. ];
  127. }
  128. public function getGoods()
  129. {
  130. return $this->hasOne(Goods::class, ['id' => 'goods_id'])->select('id,goods_name,price,original_price,cost_price,cover_pic');
  131. }
  132. public function getOrderMarketing()
  133. {
  134. return $this->hasOne(LuckyGroupOrderMarketing::class, ['order_id' => 'id'])->select('member_price_deduct,coupon_discount_money,order_id');
  135. }
  136. public function getAddress()
  137. {
  138. return $this->hasOne(UserAddress::class, ['id' => 'address_id'])->select('name,province,city,district,town,mobile,detail,id');
  139. }
  140. public function getUser()
  141. {
  142. return $this->hasOne(User::class, ['id' => 'user_id'])->select('id,avatar_url');
  143. }
  144. public function getGoodsAttr()
  145. {
  146. return $this->hasOne(GoodsAttr::class, ['id' => 'goods_attr_id'])->select('id,name');
  147. }
  148. /**
  149. * @desc:生成订单
  150. * @Author: hua
  151. * @Date: 2021/12/15
  152. * @Time: 15:14
  153. * @Copyright: copyright (c) 2021 广东七件事集团
  154. * @param $form
  155. * @return array|false
  156. */
  157. public static function createOrder($form)
  158. {
  159. $stock_locks = [];
  160. try {
  161. $group_order_goods = $form['group_order_goods'];
  162. $data = is_array($form->data) ? $form->data : Json::decode($form->data, true);
  163. $trans = Yii::$app->db->beginTransaction();
  164. $order_trade_info = [];
  165. $order_id = '';
  166. $remark = empty($data['remark'])?'':$data['remark'][0];
  167. //只有一个订单
  168. foreach ($group_order_goods as $mch_order) {
  169. $order_no = StringHelper::generateOrderNo('lgy');
  170. $mch_order_info = $mch_order['mch_order_info'];
  171. $goods = reset($mch_order['goods']);
  172. $mch_order_info['pay_money'] = bcadd($mch_order_info['goods_price'],$mch_order_info['shipping_money'],2);
  173. $order = new self();
  174. $order->loadDefaultValues();
  175. $order->load($mch_order_info, '');
  176. $order->remark = $remark;
  177. $order->activity_id = $data['activity_id'];
  178. $order->is_pay = OrderStatusEnum::NOT_PAY;
  179. $order->total_pay_price = $mch_order_info['pay_money'];
  180. $order->total_price = $mch_order_info['pay_money'];
  181. $order->money = $mch_order_info['pay_money'];
  182. $order->buy_nums = $data['num'];
  183. $order->order_no = $order_no;
  184. $order->attend_type = $data['attend_type'];
  185. $order->goods_attr_id = $data['attr_id'];
  186. $order->goods_id = $goods['goods_id'];
  187. $order->total_goods_price = $mch_order_info['goods_price'];
  188. $order->total_goods_original_price = $mch_order_info['original_goods_price'];
  189. $order->yu_attend_times = $data['yu_attend_times'];
  190. $order->consume = $data['total_expend_num'];
  191. $order->expend_type = $data['expend_type'];
  192. $order->major_data = json_encode($data);
  193. $order->address_id = $form->address_id;
  194. $res = $order->save();
  195. if ($res === false) throw new \Exception($order->getErrorMessage());
  196. $order_id = $order->id;
  197. // 订单营销
  198. if (!empty($goods['goods_details'][0]['marketing'])) {
  199. $order_marketing = new LuckyGroupOrderMarketing();
  200. $order_marketing->loadDefaultValues();
  201. $order_marketing->load($goods['goods_details'][0]['marketing'], '');
  202. $order_marketing->order_id = $order_id;
  203. $res = $order_marketing->save();
  204. if ($res === false) throw new \Exception($order_marketing->getErrorMessage());
  205. }
  206. $order_trade_info[] = [
  207. 'order_no' => $order->order_no,
  208. 'amount' => $order->total_pay_price,
  209. 'title' => mb_strlen($goods['goods_name']) > 32 ? mb_substr($goods['goods_name'], 0, 32) : $goods['goods_name'],
  210. 'notify_class' => LuckyGroupEnum::NOTIFY_CLASS,
  211. 'order_type' => PaymentTradeOrderEnum::LUCKY_GROUP_BUY_ORDER,
  212. ];
  213. }
  214. $trans->commit();
  215. LockHelper::batchUlock($stock_locks);
  216. //生成支付流水
  217. $res = \Yii::$app->services->pay->createTrade($form['mall_id'], $form['user_id'], $order_trade_info);
  218. if ($res == false) throw new \Exception('生成支付流水失败');
  219. $arr['transaction'] = $res;
  220. $arr['pay_success_page'] = Yii::$app->services->setting->mall->get($form['mall_id'], 'order.pay_after_link');// 支付成功后跳转的地址
  221. $arr['order_id'] = $order_id;
  222. return $arr;
  223. } catch (\Exception $e) {
  224. LockHelper::batchUlock($stock_locks);
  225. if (isset($trans)) $trans->rollback();
  226. self::$static_error = $e->getMessage();
  227. return false;
  228. }
  229. }
  230. public static function subUserWithholding($item)
  231. {
  232. $lucky_user = LuckyGroupUser::getGroupUser($item->mall_id, $item->user_id);
  233. if($item['consume']>0){
  234. if ($item['expend_type'] == LuckyGroupEnum::EXPEND_TYPE_SCORE) {//积分
  235. $data = ['message_id' => 0, 'mall_id' => $item->mall_id, 'user_id' => $item->user_id,
  236. 'wallet_type' => UserWalletService::WALLET_TYPE_SCORE,
  237. 'is_frozen' => false, 'unfrozen' => false, 'money' => -$item['consume'],
  238. 'desc' => '幸运拼团预扣', 'source_table' => AssetsType::LUCKY_GROUP, 'source_table_id' => $item->id,
  239. 'from_type' => UserWalletEnum::FROM_LUCKY_GROUP,
  240. 'capital_type' => UserWalletEnum::LUCKY_GROUP_CONSUME
  241. ];
  242. } else {//余额
  243. $data = ['message_id' => 0, 'mall_id' => $item->mall_id, 'user_id' => $item->user_id,
  244. 'wallet_type' => UserWalletService::WALLET_TYPE_BALANCE,
  245. 'is_frozen' => false, 'unfrozen' => false, 'money' => -$item['consume'],
  246. 'desc' => '幸运拼团预扣', 'source_table' => AssetsType::LUCKY_GROUP, 'source_table_id' => $item->id,
  247. 'from_type' => UserWalletEnum::FROM_LUCKY_GROUP,
  248. 'capital_type' => UserWalletEnum::LUCKY_GROUP_CONSUME
  249. ];
  250. }
  251. $state = UserWalletService::handle(0, $data);
  252. if ($state==false) return false;
  253. }
  254. $lucky_user->attend_times = $lucky_user->attend_times - $item['yu_attend_times'];
  255. if($lucky_user->attend_times<0) $lucky_user->attend_times = 0;
  256. return $lucky_user->save();
  257. }
  258. public static function pay($order, $pay_type)
  259. {
  260. $trans = Yii::$app->db->beginTransaction();
  261. try {
  262. $model = self::findOne(['id' => $order->id, 'status' => [StatusEnum::ENABLED]]);
  263. if (empty($model)) throw new \Exception('订单不存在或已删除');
  264. // if ($model->is_pay != OrderStatusEnum::NOT_PAY) throw new \Exception('订单已经被处理,无法进行支付');
  265. // if ($model->is_pay == StatusEnum::ENABLED) throw new \Exception('订单已支付,请不要重复支付');
  266. $model->is_pay = OrderStatusEnum::PAY;
  267. $model->group_status = LuckyGroupEnum::GROUP_JOINING;
  268. $model->payment_type = $pay_type;
  269. $model->pay_at = time();
  270. $marketing = LuckyGroupOrderMarketing::getOne([['order_id' => $model->id]]);
  271. if ($marketing && $marketing->user_coupon_id) {
  272. $use_coupon_id = \Qiniu\json_decode($marketing->user_coupon_id, true);
  273. $coupon_ids = array_keys($use_coupon_id);
  274. $coupon_state = AddonsCouponUserRelate::updateAll(['is_use' => 1], ['and', ['user_id' => $model->user_id], ['in', 'id', $coupon_ids]]);
  275. if ($coupon_state === false) throw new \Exception('修改订单失败:' . AddonsCouponUserRelate::getStaticError());
  276. }
  277. $res = $model->save();
  278. if ($res === false) throw new \Exception('修改订单失败:' . $model->getErrorMessage());
  279. // $user = LuckyGroupUser::getGroupUser($model->mall_id, $model->user_id);
  280. // $user->attend_money = bcadd($user->attend_money, $model->total_pay_price, 2);
  281. // if ($model->total_pay_price > $user->max_order_money) {
  282. // $user->max_order_money = $model->total_pay_price;
  283. // }
  284. // $user->save();
  285. $trans->commit();
  286. $attend_peoples = 1;
  287. LuckyGroupMallOpenGroupStatistics::updateStatistics(['attend_peoples' => $attend_peoples, 'mall_id' => $model->mall_id]);
  288. $statistics = [
  289. 'attend_money' => $model->total_pay_price,
  290. 'attend_peoples' => $attend_peoples,
  291. 'mall_id' => $model->mall_id,
  292. ];
  293. LuckyGroupMallStatistics::updateStatistics($statistics);
  294. // 触发订单支付事件,事务提交完成后触发
  295. $event = new LuckyGroupOrderPaidEvent($model->mall_id);
  296. $data['id'] = $model['id'];
  297. $data['operator_id'] = $params['operator_id'] ?? 0;
  298. $data['operator_name'] = $params['operator_name'] ?? '';
  299. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  300. return true;
  301. } catch (\Exception $e) {
  302. if (isset($trans)) $trans->rollback();
  303. self::$static_error = $e->getMessage();
  304. return false;
  305. }
  306. }
  307. /**
  308. * 众健酒需求增加
  309. * 获取语音播报列表
  310. * @param $mall_id
  311. * @param $activity_id
  312. * @param $goods_warehouse_id
  313. * @return array
  314. */
  315. public static function getWinText($mall_id, $activity_id, $goods_id)
  316. {
  317. // $lucky_group_customize_text_configs = (new \app\plugins\lucky_group\forms\LuckyGroupConfigForm())->textCustomizeSetting($mall_id);
  318. $list = self::find()->where(['mall_id' => $mall_id, 'activity_id' => $activity_id, 'goods_id' => $goods_id, 'group_status' => LuckyGroupEnum::GROUP_SUCCESS])
  319. ->orderBy('id desc')->limit(20)
  320. ->all();
  321. $user_list = User::find()->where(['in', 'id', array_column($list, 'user_id')])->all();
  322. $lottery_period_list = LuckyGroupLotteryPeriod::find()->where(['in', 'order_id', array_column($list, 'id')])->all();
  323. $lottery_period_arr = array_column($lottery_period_list, 'number', 'order_id');
  324. $url = ((int)$_SERVER['SERVER_PORT'] == 80 ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'];
  325. foreach ($user_list as $v) {
  326. $user_arr[$v['id']] = !empty($v['nickname']) ? $v['nickname'] : $v['username'];
  327. $avatar_url_arr[$v['id']] = !empty($v['avatar_url']) ? $v['avatar_url'] : $url . '/web/uploads/images/original/20210422/1619055025518.jpg';;
  328. }
  329. $rows = [];
  330. $join_success_text = DefaultModel::getConfig($mall_id);
  331. foreach ($list as $v) {
  332. $number = !empty($lottery_period_arr[$v['id']]) ? $lottery_period_arr[$v['id']] : 1;
  333. $nickname = !empty($user_arr[$v['user_id']]) ? $user_arr[$v['user_id']] : '';
  334. $avatar_url = !empty($avatar_url_arr[$v['user_id']]) ? $avatar_url_arr[$v['user_id']] : '';
  335. $str = "恭喜{$nickname}" . $join_success_text['customize_text_res']['join_success_text'] . "成为第{$number}期幸运儿";
  336. $rows[] = [
  337. 'text' => $str,
  338. 'avatar_url' => $avatar_url,
  339. ];
  340. }
  341. return $rows;
  342. }
  343. //去支付,再次支付调用
  344. public static function toPay($params){
  345. try {
  346. $where = ['id' => $params['id'], 'user_id' => $params['user_id'], 'mall_id' => $params['mall_id'],'group_status'=>0];
  347. $lucky_group_order = LuckyGroupOrder::findOne($where);
  348. if(empty($lucky_group_order))throw new \Exception('查无订单');
  349. $activity = LuckyGroupActivity::findOne(['id'=>$lucky_group_order->activity_id]);
  350. if(empty($activity)) throw new \Exception('活动不存在');
  351. $time = time();
  352. if(strtotime(date('Y-m-d ').$activity->end_time)<$time) throw new \Exception('活动已过期');
  353. $goods = Goods::findOne(['id'=>$lucky_group_order->goods_id]);
  354. $orders[] = [
  355. 'order_id' => $lucky_group_order->id,
  356. 'order_no' => $lucky_group_order->order_no,
  357. 'amount' => $lucky_group_order->total_pay_price,
  358. 'title' => mb_strlen($goods['goods_name']) > 32 ? mb_substr($goods['goods_name'], 0, 32) : $goods['goods_name'],
  359. 'notify_class' => LuckyGroupEnum::NOTIFY_CLASS,
  360. 'order_type' => PaymentTradeOrderEnum::LUCKY_GROUP_BUY_ORDER,
  361. ];
  362. $payment_expire_time = Yii::$app->services->setting->mall->get($params['mall_id'], 'order.payment_expire_time');
  363. if(empty($payment_expire_time)) $payment_expire_time = 30;
  364. $order_time = $lucky_group_order['created_at']+$payment_expire_time*60;
  365. //生成支付流水
  366. $res = \Yii::$app->services->pay->createTrade($params['mall_id'], $params['user_id'], $orders);
  367. if ($res == false) throw new \Exception('生成支付流水失败');
  368. $res['surplus_second'] = 30*60;//剩余秒数
  369. if(!empty($order_time)) $res['surplus_second'] = ($order_time - time())>0?$order_time - time():0;
  370. return $res;
  371. }catch (\Exception $e){
  372. self::$static_error = $e->getMessage();
  373. return false;
  374. }
  375. }
  376. public static function close($params){
  377. try {
  378. $where = ['id' => $params['id'], 'user_id' => $params['user_id'], 'mall_id' => $params['mall_id'],'group_status'=>0,'pay_at'=>0];
  379. $lucky_group_order = LuckyGroupOrder::findOne($where);
  380. if(empty($lucky_group_order))throw new \Exception('查无订单');
  381. $lucky_group_order->group_status = -1;
  382. $res = $lucky_group_order->save();
  383. if($res){
  384. //如果遇到组合支付订单,查看是否余额/积分支付,原路返回
  385. $groupOrders = PaymentTradeGroupOrder::find()->where(['order_no'=>$lucky_group_order->order_no,'is_pay'=>1])->asArray()->all();
  386. if($groupOrders){
  387. $insert_wallet = [];
  388. foreach($groupOrders as $groupOrder){
  389. if(in_array($groupOrder['pay_type'],[PayTypeEnum::BALANCE,PayTypeEnum::SCORE])){
  390. $wallet_type = $groupOrder['pay_type'] == PayTypeEnum::SCORE ? UserWalletService::WALLET_TYPE_SCORE : UserWalletService::WALLET_TYPE_BALANCE;
  391. $trade = PaymentTrade::findOne($groupOrder['trade_id']);
  392. $insert_wallet[] = [
  393. 'mall_id' => $lucky_group_order->mall_id,
  394. 'user_id' => $lucky_group_order->user_id,
  395. 'is_frozen' => false,
  396. 'wallet_type' => $wallet_type,// 暂定退回余额或积分
  397. 'money' => $trade->pay_fee,
  398. 'desc' => '订单退款',
  399. 'source_table' => PaymentTradeGroupOrder::tableName(),
  400. 'source_table_id' => $groupOrder['id'],
  401. 'from_type' => UserWalletEnum::REFUND,
  402. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  403. 'order_no' => $lucky_group_order->order_no,
  404. 'not_add_total' => true
  405. ];
  406. }
  407. }
  408. if($insert_wallet){
  409. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  410. if (empty($res)) throw new \Exception(UserWalletService::getStaticError());
  411. }
  412. }
  413. }
  414. return true;
  415. }catch (\Exception $e){
  416. self::$static_error = $e->getMessage();
  417. return false;
  418. }
  419. }
  420. }