WinLossLogic.php 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. <?php
  2. namespace addons\LuckyGroup\common\logic\order;
  3. use addons\CardCoupons\common\models\CardCouponsScoreGive;
  4. use addons\LuckyGroup\common\enums\LuckyGroupEnum;
  5. use addons\LuckyGroup\common\logic\customize\Customize;
  6. use addons\LuckyGroup\common\logic\reward\GroupRewardLogic;
  7. use addons\LuckyGroup\common\models\LuckyGroupActivityStatistics;
  8. use addons\LuckyGroup\common\models\LuckyGroupBonusPool;
  9. use addons\LuckyGroup\common\models\LuckyGroupFrozenLog;
  10. use addons\LuckyGroup\common\models\LuckyGroupGoods;
  11. use addons\LuckyGroup\common\models\LuckyGroupLevel;
  12. use addons\LuckyGroup\common\models\LuckyGroupLotteryPeriod;
  13. use addons\LuckyGroup\common\models\LuckyGroupMallOpenGroupStatistics;
  14. use addons\LuckyGroup\common\models\LuckyGroupMallStatistics;
  15. use addons\LuckyGroup\common\models\LuckyGroupOpenRewardLog;
  16. use addons\LuckyGroup\common\models\LuckyGroupOrder;
  17. use addons\LuckyGroup\common\models\LuckyGroupUser;
  18. use addons\LuckyGroup\common\models\LuckyGroupWinLog;
  19. use addons\LuckyGroup\common\queue\OrderPaidQueueLogic;
  20. use addons\LuckyGroup\common\logic\notify\Notify;
  21. use common\enums\AddonsEnum;
  22. use common\enums\GoodsTypeEnum;
  23. use common\enums\OrderStatusEnum;
  24. use common\enums\PaymentTradeOrderEnum;
  25. use common\enums\PayTypeEnum;
  26. use common\enums\RefundStatusEnum;
  27. use common\enums\ShippingTypeEnum;
  28. use common\enums\StatusEnum;
  29. use common\enums\UserWalletEnum;
  30. use common\globals\GlobalInvoke;
  31. use common\helpers\DateHelper;
  32. use common\helpers\StringHelper;
  33. use common\models\common\CapitalLog;
  34. use common\models\common\PaymentTrade;
  35. use common\models\common\PaymentTradeItem;
  36. use common\models\common\PaymentTradeOrder;
  37. use common\models\goods\Goods;
  38. use common\models\goods\GoodsAttr;
  39. use common\models\mall\MallAddons;
  40. use common\models\order\Order;
  41. use common\models\order\OrderDetail;
  42. use common\models\order\OrderRefund;
  43. use common\models\user\User;
  44. use common\models\user\UserAddress;
  45. use common\models\user\UserPartitionRelationship;
  46. use common\models\user\UserWallet;
  47. use services\common\UserWalletService;
  48. use yii\db\Exception;
  49. class WinLossLogic
  50. {
  51. public function openReward($activity, $goods_id, $order_ids)
  52. {
  53. if (empty($order_ids)) return false;
  54. $t = \Yii::$app->db->beginTransaction();
  55. try {
  56. $mall_id = $activity['mall_id'];
  57. $order_arr = $order_ids;
  58. //中文配置
  59. $customize_text = \Yii::$app->services->setting->addons->get($mall_id, LuckyGroupEnum::ADDONS_NAME, 'customize_text');
  60. $customize_reward_name = \Yii::$app->services->setting->addons->get($mall_id, LuckyGroupEnum::ADDONS_NAME, 'customize_reward_name');
  61. $customize_arr = array_merge($customize_text, $customize_reward_name);
  62. // 获取同一拼团同一商品,参团中的订单,抽中次数小于购买次数的订单 - 按时间顺序取拼团数量
  63. $group_require_num = $activity['group_num']; //开团需要人数
  64. $virtual_num = $activity['virtual_num']; //活动设置的虚拟人数
  65. $real_group_require_num = $group_require_num - $virtual_num; //真实开团需要人数
  66. $real_group_require_num = $real_group_require_num < 0 ? $group_require_num : $real_group_require_num;
  67. $configs['basic'] = \Yii::$app->services->setting->addons->get($mall_id, LuckyGroupEnum::ADDONS_NAME, 'basic');
  68. $configs['bonus_pool'] = \Yii::$app->services->setting->addons->get($mall_id, LuckyGroupEnum::ADDONS_NAME, 'bonus_pool');
  69. $joining_orders = LuckyGroupOrder::find()
  70. ->where(['id' => $order_ids, 'group_status' => 1])
  71. ->andWhere(new \yii\db\Expression('`buy_nums` > `win_nums`'))
  72. ->indexBy('id')
  73. ->all();
  74. $attend_order_num = count($joining_orders); //本次可开奖订单数(人数)
  75. echo '<pre>';
  76. var_dump(
  77. '开团需要人数:' . $group_require_num,
  78. '活动设置的虚拟人数:' . $virtual_num,
  79. '真实开团需要人数:' . $real_group_require_num,
  80. '本次可开奖订单数(人数):' . $attend_order_num
  81. );
  82. // 随机拼中人数
  83. $random_win_num = ($activity['random_win_num'] <= 0) ? 1 : $activity['random_win_num'];
  84. if ($attend_order_num < $random_win_num) {
  85. //先删除订单id入队标记
  86. $queue_logic = new OrderPaidQueueLogic();
  87. $queue_logic->delOrderEnqueueFlag($order_arr);
  88. $order_id_enqueue = array_column($joining_orders, 'id');
  89. $queue_logic->enqueue($mall_id, $activity, $goods_id, $order_id_enqueue);
  90. return false;
  91. }
  92. $user_id_arr = array_column($joining_orders, 'user_id');
  93. $lottery_rules = new LotteryRulesLogic();
  94. $win_keys = $lottery_rules->checkWinOrLoss($mall_id, $joining_orders, $user_id_arr, $attend_order_num, $random_win_num, $configs['basic']);
  95. if (!is_array($win_keys)) $win_keys = [$win_keys];
  96. $this->updateGroupGoodsJoinNum($activity['id'], $goods_id, $attend_order_num);
  97. $group_users = LuckyGroupUser::lists(
  98. [
  99. 'where' => [
  100. ['user_id' => $user_id_arr]
  101. ],
  102. 'index' => 'user_id'
  103. ],
  104. false
  105. );
  106. $users = User::lists(
  107. [
  108. 'where' => [
  109. ['id' => $user_id_arr]
  110. ],
  111. 'index' => 'id'
  112. ]
  113. );
  114. $user_wallets = UserWallet::lists(
  115. [
  116. 'where' => [
  117. ['user_id' => $user_id_arr]
  118. ],
  119. 'index' => 'user_id'
  120. ],
  121. false
  122. );
  123. $win_log_list = LuckyGroupWinLog::lists([
  124. 'where' => [
  125. ['mall_id' => $mall_id],
  126. ['activity_id' => $activity['id']],
  127. ['status' => StatusEnum::ENABLED],
  128. ['user_id' => $user_id_arr],
  129. ],
  130. 'select' => 'user_id,sum(win_num) as win_num',
  131. 'index' => 'user_id',
  132. 'group' => 'user_id'
  133. ]);
  134. $expend_type = $activity['expend_type'] ?? ''; // 参团消耗类型
  135. $expend_num = $activity['expend_num'] ?? 0; // 参团消耗数量
  136. $yu_attend_times = $consume = 0;
  137. $order_ids = $win_order_ids = $loss_order_ids = '';
  138. $insert_wallet = $frozen_log_arr = $user_frozen_arr = $order_id_enqueue = [];
  139. $time = time();
  140. $attend_award_setting = json_decode($activity['attend_award_setting'], true);
  141. $win_award_setting = json_decode($activity['win_award_setting'], true);
  142. $bonus_pool = LuckyGroupBonusPool::findOne(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED]);
  143. $user_address_id_arr = [];
  144. $order_no_arr = [];
  145. $goods_attr_id = 0;
  146. foreach ($joining_orders as $key => $order) {
  147. $goods_attr_id = $order['goods_attr_id'];
  148. if (in_array($key, $win_keys)) {
  149. $user_address_id_arr[] = $order['address_id'];
  150. $order_no_arr[] = $order['order_no'];
  151. }
  152. }
  153. $pay_order_list = PaymentTradeOrder::lists(['where' => [['order_no' => $order_no_arr],['is_pay'=>1]], 'index' => 'order_no']);
  154. $trade_id_arr = array_column($pay_order_list, 'trade_id');
  155. $pay_trade_list = PaymentTrade::lists(['where' => [['id' => $trade_id_arr]], 'index' => 'id']);
  156. $user_address_arr = UserAddress::lists(['where' => [['id' => $user_address_id_arr]], 'index' => 'user_id']);
  157. $goods_attr = GoodsAttr::findOne(['id' => $goods_attr_id]);
  158. $goods = Goods::findOne(['id' => $goods_id]);
  159. $user_arr = [];
  160. $trans_pool_money = $subsidy = 0;
  161. $lottery_period_arr = [];
  162. $lottery_period = LuckyGroupLotteryPeriod::getOne([['activity_id' => $activity['id']], ['goods_id' => $goods_id]], false, [], 'number desc');
  163. $number = $lottery_period['number'] ?? 0;
  164. $win_money = $win_peoples = $attend_reward = 0;
  165. $loss_money = 0;
  166. $loss_peoples = 0;
  167. $attend_reward_amounts = 0;
  168. $win_reward_scores = 0;
  169. $win_reward_balances = 0;
  170. $group_failed_times = 0;
  171. $deplete_balances = 0;
  172. $deplete_scores = 0;
  173. $goods_price = 0;
  174. // 因为主商城订单在事务中。会导致触发事件时订单详情,所以需要一个数组存储
  175. $mainOrderList = [];
  176. foreach ($joining_orders as $key => $order) {
  177. $user_arr[$order['user_id']]['order_id'] = $order['id'];
  178. $goods_price = bcdiv($order['total_goods_price'] , $order['buy_nums'], 2);
  179. $order_ids .= $order['id'] . ',';
  180. //判断预扣参团次数是否大于0,是直接减预扣
  181. $attend_times = 0;
  182. if ($order['yu_attend_times'] <= 0) {
  183. $res = $this->reduceAttendTimes($group_users, $order, $customize_arr);
  184. if ($res == false) {
  185. unset($joining_orders[$key]);
  186. var_dump(3);
  187. continue;
  188. }
  189. $attend_times = 1;
  190. } else {
  191. $yu_attend_times = -1;
  192. }
  193. //消耗
  194. if ($expend_num > 0) {
  195. if ($order['consume'] <= 0) {
  196. $res = $this->reduceExpendNum($expend_type, $expend_num, $user_wallets, $order, $deplete_balances, $deplete_scores);
  197. if ($res == false) {
  198. unset($joining_orders[$key]);
  199. var_dump(4);
  200. continue;
  201. }
  202. } else {
  203. $consume = -$expend_num;
  204. if ($expend_type == LuckyGroupEnum::AWARD_TYPE_BALANCE) $deplete_balances += $expend_num;
  205. if ($expend_type == LuckyGroupEnum::AWARD_TYPE_SCORE) $deplete_scores += $expend_num;
  206. }
  207. }
  208. $res = $this->updateLuckyGroupUser($group_users[$order['user_id']], $order, $attend_times);
  209. if ($res == false) {
  210. unset($joining_orders[$key]);
  211. var_dump(2);
  212. continue;
  213. }
  214. $res = $this->updateLuckyOrder($order, $yu_attend_times, $consume);
  215. if ($res == false) {
  216. unset($joining_orders[$key]);
  217. var_dump(1);
  218. continue;
  219. }
  220. $win_user_id = $win_order_id = $loss_user_id = $loss_order_id = 0;
  221. $unit_price = $order['total_pay_price'] / $order['buy_nums'];
  222. if (in_array($key, $win_keys)) {
  223. //生成主订单
  224. $main_order = self::createMallOrder($order, $pay_order_list, $pay_trade_list, $user_address_arr, $goods, $goods_attr);
  225. if ($main_order == false) {
  226. //生成主订单失败,退款
  227. //todo 退用户余额并修改状态为拼团失败
  228. $desc = ('幸运拼团生成主订单失败');
  229. var_dump('order_id:' . $order['id'] . $desc);
  230. $order->group_status = 3;
  231. $group_buy_text = Customize::to($customize_arr, 'group_buy_text');
  232. $desc = ($group_buy_text ?? '幸运拼团') . '生成主订单失败';
  233. $this->refundToBalance($order, $desc);
  234. continue;
  235. }
  236. //拼中
  237. $group_users[$order['user_id']]->win_num += 1;
  238. $group_users[$order['user_id']]->save();
  239. $order->group_status = LuckyGroupEnum::GROUP_SUCCESS;
  240. $order->win_time = time();
  241. $order->win_nums = $order->win_nums + 1;
  242. //如果类型为自动拼团,并且购买数量大于中奖次数,则自动继续参与拼团
  243. if ($order->attend_type == LuckyGroupEnum::ATTEND_TYPE_AUTO
  244. && $order->buy_nums > $order->win_nums) {
  245. $order->group_status = LuckyGroupEnum::GROUP_JOINING;
  246. $order_id_enqueue[] = $order['id'];
  247. }
  248. $order->save();
  249. $win_order_id = $order['id'];
  250. $win_user_id = $order['user_id'];
  251. $win_order_ids .= $win_order_id . ',';
  252. $user_win_times = $win_log_list[$order['user_id']]['win_num'] ?? 0;
  253. //生成拼中奖励
  254. GroupRewardLogic::computeWinReward($win_award_setting, $mall_id, $win_user_id, $win_order_id, $activity['win_times'], $user_win_times+1, $insert_wallet, $win_reward_scores, $win_reward_balances, $customize_arr,
  255. $order['order_no'],$order['user_id'],$goods_price,$goods['goods_name']);
  256. try {
  257. $mainOrderList[] = [
  258. 'id' => $main_order['id'],
  259. 'operator_id' => $main_order['user_id'],
  260. 'operator_name' => $users[$main_order['user_id']]['nickname'] ?? '',
  261. ];
  262. } catch (\Exception $e) {
  263. var_dump($e->getMessage());
  264. }
  265. GroupRewardLogic::computePrizePool($configs, $order['id'], $group_users[$order['user_id']], $time, $bonus_pool['money'] ?? 0, $frozen_log_arr, $user_frozen_arr, $customize_arr);
  266. $pool_trans_money = $activity['pool_trans_money']; //转到奖金池金额%
  267. $is_percent_pool_trans = $activity['is_percent_pool_trans']; //是否按百分比算分配奖金池金额【1是 0否】
  268. if ($pool_trans_money > 0) {
  269. //计算转入奖金池金额,除以拼中订单购买数量
  270. $trans_pool_money += $is_percent_pool_trans ? ($unit_price * $pool_trans_money / 100) : $pool_trans_money;
  271. }
  272. $win_money += $unit_price; //拼中金额
  273. $win_peoples++;//拼中人数
  274. LuckyGroupWinLog::setData(['mall_id'=>$mall_id,'user_id'=>$order['user_id'],'activity_id'=>$activity['id'],'win_num'=>1]);
  275. } else {
  276. $group_failed_times += 1;
  277. //拼不中
  278. if ($order['attend_type'] == LuckyGroupEnum::ATTEND_TYPE_AUTO) {
  279. $order_id_enqueue[] = $order['id'];
  280. LuckyGroupWinLog::setData(['mall_id'=>$mall_id,'user_id'=>$order['user_id'],'activity_id'=>$activity['id'],'win_num'=>0]);
  281. } else {
  282. //拼团失败,退款
  283. //todo 退用户余额并修改状态为拼团失败
  284. $order->group_status = 3;
  285. $group_buy_order_text = Customize::to($customize_arr, 'group_buy_order_text');
  286. $join_team_text = Customize::to($customize_arr, 'join_team_text');
  287. $desc = ($group_buy_order_text ?? '幸运拼团订单') . '(id:' . $order['id'] . ')' . ($join_team_text ?? '拼团') . '失败,退还购物金额';
  288. var_dump('order_id:' . $order['id'] . $desc);
  289. $this->refundToBalance($order, $desc);
  290. $loss_order_id = $order['id'];
  291. $loss_user_id = $order['user_id'];
  292. $loss_order_ids .= $loss_order_id . ',';
  293. $loss_money += $unit_price;
  294. $loss_peoples++;//拼不中人数
  295. }
  296. }
  297. $attend_reward_amounts += $unit_price;
  298. //生成参团奖励数据
  299. GroupRewardLogic::computeJoinReward($attend_award_setting, $activity['attend_user_type'], $mall_id,
  300. $order['user_id'], $order['id'], $win_user_id, $win_order_id, $loss_user_id, $loss_order_id, $insert_wallet, $customize_arr, $attend_reward,
  301. $order['order_no'],$order['user_id'],$goods_price,$goods['goods_name']
  302. );
  303. //生成推荐参团奖励数量
  304. GroupRewardLogic::computeInviteReward($mall_id, $goods_price, $configs, $order['id'], $users[$order['user_id']], $insert_wallet, $customize_arr,
  305. $order['order_no'],$order['user_id'],$goods['goods_name']);
  306. }
  307. if (!empty($insert_wallet)) {
  308. $res = $this->sendUserWallet($insert_wallet, $group_users);
  309. if ($res == false) throw new Exception('发送资金队列失败');
  310. }
  311. //开团日志
  312. $params = [
  313. 'mall_id' => $mall_id, 'goods_id' => $goods_id, 'group_require_num' => $group_require_num,
  314. 'real_group_require_num' => $real_group_require_num,
  315. 'activity_id' => $activity['id'], 'expend_type' => $expend_type, 'expend_num' => $expend_num,
  316. 'virtual_num' => $virtual_num, 'attend_order_num' => $attend_order_num, 'order_id' => trim($order_ids, ','),
  317. 'win_order_ids' => trim($win_order_ids, ','), 'win_reward_scores' => $win_reward_scores, 'win_reward_balances' => $win_reward_balances,
  318. 'attend_reward_amounts' => $attend_reward_amounts,
  319. 'distribute_bonus_pool_amounts' => $trans_pool_money,
  320. ];
  321. $open_reward_log_model = LuckyGroupOpenRewardLog::setData($params);
  322. if ($open_reward_log_model == false) throw new Exception('添加开团日志失败');
  323. //生成奖金池冻结数据
  324. if (!empty($frozen_log_arr)) {
  325. LuckyGroupFrozenLog::batchAdd($frozen_log_arr);
  326. if (!empty($user_frozen_arr)) {
  327. foreach ($user_frozen_arr as $user_id => $money) {
  328. $group_users[$user_id]->frozen_commission += $money;
  329. $group_users[$user_id]->pool_commission += $money;
  330. $group_users[$user_id]->save();
  331. }
  332. }
  333. }
  334. // 参团等级佣金奖励发放
  335. if(MallAddons::isInstall($mall_id,AddonsEnum::ADDONS_NAME_AGENT)){
  336. GroupRewardLogic::sendGroupCommissionLog($configs, $joining_orders, $user_arr, $activity,$goods_price,$goods['goods_name']);
  337. }
  338. //先删除订单id入队标记
  339. $queue_logic = new OrderPaidQueueLogic();
  340. $queue_logic->delOrderEnqueueFlag($order_arr);
  341. if ($order_id_enqueue) {
  342. var_dump('重新入队订单id:' . implode(',', $order_id_enqueue));
  343. $queue_logic->enqueue($mall_id, $activity, $goods_id, $order_id_enqueue);
  344. }
  345. if ($trans_pool_money > 0) {
  346. LuckyGroupBonusPool::setData($bonus_pool, $mall_id, $trans_pool_money);
  347. }
  348. $send_sms_order_ids = [];
  349. foreach ($joining_orders as $key => $order) {
  350. $lottery_period_arr[] = [
  351. $order['mall_id'],
  352. $open_reward_log_model['id'],
  353. $order['activity_id'],
  354. $order['goods_id'],
  355. $order['user_id'],
  356. $order['id'],
  357. $number + 1,
  358. trim($win_order_ids, ','),
  359. trim($loss_order_ids, ','),
  360. StatusEnum::ENABLED,
  361. $time,
  362. $time,
  363. ];
  364. $send_sms_order_ids = $order['id'];
  365. }
  366. //添加期数
  367. LuckyGroupLotteryPeriod::setData($lottery_period_arr);
  368. //活动统计
  369. $params = [
  370. 'activity_id' => $activity['id'],
  371. 'mall_id' => $mall_id,
  372. 'group_amounts' => $attend_reward_amounts,
  373. 'group_peoples' => $random_win_num,
  374. 'group_periods' => 1,
  375. 'group_failed_times' => $group_failed_times,
  376. 'deplete_balances' => $deplete_balances,
  377. 'deplete_scores' => $deplete_scores,
  378. 'win_reward_balances' => $win_reward_balances,
  379. 'win_reward_scores' => $win_reward_scores,
  380. 'distribute_bonus_pool_amounts' => $trans_pool_money,
  381. ];
  382. $res = LuckyGroupActivityStatistics::updateStatistics($params);
  383. //商城统计,按月
  384. $params = [
  385. 'win_money' => $win_money,
  386. 'win_peoples' => $win_peoples,
  387. 'attend_commission' => $attend_reward,
  388. 'mall_id' => $mall_id,
  389. ];
  390. var_dump('商城统计,按月');
  391. var_dump($params);
  392. $res = LuckyGroupMallStatistics::updateStatistics($params);
  393. //商城统计,按日
  394. $params = [
  395. 'win_money' => $win_money,
  396. 'loss_money' => $loss_money,
  397. 'win_peoples' => $win_peoples,
  398. 'loss_peoples' => $loss_peoples,
  399. ];
  400. $params['mall_id'] = $mall_id;
  401. $res = LuckyGroupMallOpenGroupStatistics::updateStatistics($params);
  402. $t->commit();
  403. if (!empty($mainOrderList)) { // 因为受到事务影响导致。执行队列事件是可能主商城订单详情并未创建成功。所以移动到事务外
  404. $event = new \common\events\order\OrderPaidEvent($mall_id);
  405. foreach ($mainOrderList as $main_order) {
  406. $main_data['id'] = $main_order['id'];
  407. $main_data['operator_id'] = $main_order['operator_id'] ?? 0;
  408. $main_data['operator_name'] = $main_order['operator_name'];
  409. \Yii::$app->services->events->dispatch($event, $main_data, $event->listeners);
  410. }
  411. }
  412. var_dump('开团成功');
  413. $send_service = new Notify();
  414. //发送短信通知 成功或失败
  415. $order_ids = explode(',', trim($order_ids, ','));
  416. \Yii::$app->custom->logs(__METHOD__ . "幸运拼团发送短信 " . json_encode($order_ids));
  417. if ($order_ids) {
  418. foreach ($order_ids as $order_id) {
  419. \Yii::$app->custom->logs("幸运拼团发送短信 " . $order_id);
  420. $send_service->send(['id' => $order_id]);
  421. }
  422. }
  423. return true;
  424. } catch (\Exception $e) {
  425. $t->rollBack();
  426. var_dump('openReward:err:' . $e->getMessage() . $e->getFile().$e->getLine());
  427. return false;
  428. }
  429. }
  430. public function sendUserWallet($insert_wallet, $group_users)
  431. {
  432. $mall_id = 0;
  433. // 批量插入数据
  434. try {
  435. //投递到资产队列
  436. if ($insert_wallet) {
  437. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  438. if (empty($res)) throw new \Exception(UserWalletService::getStaticError());
  439. $arr = [];
  440. var_dump('sendUserWallet====insert_wallet'.json_encode($insert_wallet));
  441. foreach ($insert_wallet as $k => $item) {
  442. $wallet_type = $item['wallet_type'];
  443. $mall_id = $item['mall_id'];
  444. if (isset($arr[$item['user_id']])) {
  445. if (isset($arr[$item['user_id']][$wallet_type])) {
  446. $arr[$item['user_id']][$wallet_type] += $item['money'];
  447. } else {
  448. $arr[$item['user_id']][$wallet_type] = $item['money'];
  449. }
  450. } else {
  451. $arr[$item['user_id']] = [
  452. $wallet_type => $item['money'],
  453. ];
  454. }
  455. }
  456. var_dump('sendUserWallet====arr'.json_encode($arr));
  457. foreach ($arr as $user_id => $value) {
  458. if (isset($group_users[$user_id])) {
  459. var_dump($user_id.'更新前'.$group_users[$user_id]->balance);
  460. foreach ($value as $wallet_type => $money) {
  461. $group_users[$user_id]->$wallet_type += $money;
  462. }
  463. $res = $group_users[$user_id]->save();
  464. var_dump($user_id.'更新后'.$group_users[$user_id]->balance);
  465. if (empty($res)) throw new \Exception('更新团长表奖励数额报错');
  466. }else{
  467. $lg_user = LuckyGroupUser::findOne(['user_id'=>$user_id,'mall_id'=>$mall_id,'status'=>1]);
  468. if(empty($lg_user)){
  469. $post = ['user_id' => $user_id, 'level' => 0];
  470. foreach ($value as $wallet_type => $money) {
  471. $post[$wallet_type] = $money;
  472. }
  473. LuckyGroupUser::setData($mall_id, $post);
  474. }else{
  475. var_dump($user_id.'==更新前'.$lg_user->balance);
  476. foreach ($value as $wallet_type => $money) {
  477. $lg_user->$wallet_type += $money;
  478. }
  479. $lg_user->save();
  480. var_dump($user_id.'==更新后'.$lg_user->balance);
  481. }
  482. }
  483. }
  484. }
  485. return true;
  486. } catch (\Exception $e) {
  487. echo '开团发送资金' . $e->getMessage();
  488. return false;
  489. }
  490. }
  491. //更新团长数据
  492. public function updateLuckyGroupUser($group_user, $order, $attend_times)
  493. {
  494. $group_user->has_attend_times += 1;
  495. $group_user->attend_times -= $attend_times;
  496. if ($group_user->attend_times < 0) $group_user->attend_times = 0;
  497. $res = $group_user->save();
  498. if ($res == false) {
  499. $order->group_status = 3;
  500. $this->refundToBalance($order, '更新团长数据失败');
  501. return false;
  502. }
  503. return true;
  504. }
  505. //更新订单数据
  506. public function updateLuckyOrder($order, $yu_attend_times, $consume)
  507. {
  508. if (!empty($yu_attend_times)) $order->yu_attend_times += $yu_attend_times;
  509. if (!empty($consume)) $order->consume += $consume;
  510. $order->open_times += 1;
  511. $res = $order->save();
  512. if ($res == false) {
  513. $this->refundToBalance($order, '更新订单数据失败');
  514. return false;
  515. }
  516. return true;
  517. }
  518. //扣减参团次数
  519. public function reduceAttendTimes($group_users, $order, $customize_arr)
  520. {
  521. $group_buy_order_text = Customize::to($customize_arr, 'group_buy_order_text');
  522. $join_into_team_text = Customize::to($customize_arr, 'join_into_team_text');
  523. $desc = ($group_buy_order_text ?? '幸运拼团订单') . ',' . ($join_into_team_text ?? '参团') . '次数不足';
  524. try {
  525. if ($group_users[$order['user_id']]['attend_times'] <= 0) {
  526. throw new Exception($desc);
  527. }
  528. return true;
  529. } catch (\Exception $e) {
  530. $order->group_status = 3;
  531. $this->refundToBalance($order, $desc);
  532. return false;
  533. }
  534. }
  535. //扣减消耗
  536. public function reduceExpendNum($expend_type, $expend_num, $user_wallets, &$order, &$deplete_balances, &$deplete_scores, $customize_arr = [])
  537. {
  538. $group_buy_order_text = Customize::to($customize_arr, 'group_buy_order_text');
  539. $join_into_team_text = Customize::to($customize_arr, 'join_into_team_text');
  540. try {
  541. $user_money = 0;
  542. if (!empty($expend_type) && !empty($expend_num)) {
  543. $is_can_continue = true;
  544. switch ($expend_type) {
  545. case LuckyGroupEnum::AWARD_TYPE_BALANCE:
  546. // 消耗余额
  547. $user_money = $user_wallets[$order['user_id']]->$expend_type;
  548. break;
  549. case LuckyGroupEnum::AWARD_TYPE_SCORE:
  550. // 消耗积分
  551. $user_money = $user_wallets[$order['user_id']]->$expend_type;
  552. break;
  553. default:
  554. }
  555. //判断积分、余额、货币是否足够
  556. if ($is_can_continue === false || $user_money < $expend_num) throw new Exception('拼团消耗不足');
  557. //参团消耗扣减
  558. if (in_array($expend_type, [LuckyGroupEnum::AWARD_TYPE_BALANCE, LuckyGroupEnum::AWARD_TYPE_SCORE])) {
  559. //积分余额消耗
  560. if ($expend_num > 0) {
  561. $expend_name = '';
  562. if ($expend_type == LuckyGroupEnum::AWARD_TYPE_BALANCE) {
  563. $expend_name = '余额';
  564. $deplete_balances += $expend_num;
  565. }
  566. if ($expend_type == LuckyGroupEnum::AWARD_TYPE_SCORE) {
  567. $expend_name = '积分';
  568. $deplete_scores += $expend_num;
  569. }
  570. $desc = ($group_buy_order_text ?? '幸运拼团订单') . '(' . $order['id'] . ')' . ($join_into_team_text ?? '参团') . '成功,扣减' . $expend_name . $expend_num;
  571. $data = [
  572. 'message_id' => 0,
  573. 'not_add_total' => true,
  574. 'mall_id' => $order['mall_id'], 'user_id' => $order['user_id'],
  575. 'is_frozen' => false,
  576. 'wallet_type' => $expend_type,
  577. 'money' => -$expend_num,
  578. 'desc' => $desc,
  579. 'source_table' => $extension['source_table'] ?? '',
  580. 'source_table_id' => $extension['source_table_id'] ?? 0,
  581. 'from_type' => LuckyGroupEnum::ADDONS_NAME,
  582. 'capital_type' => UserWalletEnum::LUCKY_GROUP_CONSUME,
  583. ];
  584. $res = UserWalletService::handle(0, $data);
  585. if ($res == false) {
  586. $desc = ($group_buy_order_text ?? '幸运拼团订单') . ($join_into_team_text ?? '参团') . '扣减' . $expend_name . '失败';
  587. throw new Exception($desc);
  588. }
  589. }
  590. }
  591. }
  592. return true;
  593. } catch (\Exception $e) {
  594. $order->group_status = 3;
  595. $this->refundToBalance($order, $e->getMessage());
  596. return false;
  597. }
  598. }
  599. //更新参团次数
  600. public function updateGroupGoodsJoinNum($activity_id, $goods_id, $attend_order_num)
  601. {
  602. $lucky_group_goods = LuckyGroupGoods::findOne(['activity_id' => $activity_id, 'goods_id' => $goods_id]);
  603. if(!$lucky_group_goods) throw new \Exception('activity_id:'.$activity_id.',goods_id:'.$goods_id.' not exist');
  604. $open_times = $lucky_group_goods->open_times;
  605. $lucky_group_goods->open_times++;
  606. $lucky_group_goods->join_num = $lucky_group_goods->join_num + $attend_order_num;
  607. $res = $lucky_group_goods->save();
  608. if ($res == false) throw new Exception($lucky_group_goods->getErrorMessage());
  609. return $open_times;
  610. }
  611. public function refundToBalance($order, $desc = '', $wallet_type = 'balance')
  612. {
  613. // 开启事务
  614. $transaction = \Yii::$app->db->beginTransaction();
  615. $single_prize = bcdiv($order['total_pay_price'], $order['buy_nums'], 2) + 0;
  616. $refund_money = ($order['buy_nums'] - $order['win_nums']) * $single_prize; // 退款金额
  617. if ($refund_money <= 0) {
  618. \Yii::$app->custom->logs('退款金额小于等于0不进行退款', $order['order_no']);
  619. return true;
  620. }
  621. try {
  622. // 查看当前订单是否存在售后表中
  623. $refund = OrderRefund::find()->where(['order_no' => $order['order_no'], 'status' => StatusEnum::ENABLED])->exists();
  624. if ($refund) {
  625. \Yii::$app->custom->logs('幸运拼团退款订单已存在', $order['order_no']);
  626. return true;
  627. }
  628. // 不存在则创建退款订单
  629. $refundModel = new OrderRefund();
  630. $refundModel->mall_id = $order['mall_id'];
  631. $refundModel->user_id = $order['user_id'];
  632. $refundModel->order_id = $order['id'];
  633. $refundModel->order_no = $order['order_no'];
  634. $refundModel->type = 1;
  635. $refundModel->reason = '幸运拼团失败退款';
  636. $refundModel->refund_price = $refund_money;
  637. $refundModel->reality_refund_price = $refund_money;
  638. $refundModel->refund_at = time();
  639. $refundModel->num = ($order['buy_nums'] - $order['win_nums']);
  640. $refundModel->step_status = RefundStatusEnum::STEP_6;
  641. $refundModel->refund_status = RefundStatusEnum::FINISH;
  642. $res = $refundModel->save();
  643. if (!$res) {
  644. \Yii::$app->custom->logs('课程退款订单创建失败', OrderRefund::getStaticError());
  645. return true;
  646. }
  647. // 查询支付单号
  648. $trade_id = PaymentTradeOrder::find()->where(['order_no' => $order['order_no'], 'order_type' => PaymentTradeOrderEnum::LUCKY_GROUP_BUY_ORDER])
  649. ->select('trade_id')->scalar();
  650. if (empty($trade_id)) {
  651. \Yii::$app->custom->logs('课程退款订单不存在', $order['order_no']);
  652. return true;
  653. }
  654. $payment_trade = PaymentTrade::findOne(['id' => $trade_id]);
  655. $list = PaymentTrade::find()->where(['id' => $trade_id, 'is_pay' => 1])->all();
  656. if ($payment_trade->isMixedPay()) { // 组合支付退款
  657. $list = PaymentTradeItem::find()->where(['trade_id' => $trade_id, 'is_pay' => 1])->all();
  658. }
  659. $insert_wallet = [];
  660. $params = [
  661. 'id' => $refundModel->id,
  662. 'step_status' => RefundStatusEnum::STEP_6
  663. ];
  664. foreach ($list as $item) {
  665. if (in_array($item->pay_type, [PayTypeEnum::BALANCE, PayTypeEnum::SCORE])) { // 余额-积分退款
  666. $wallet_type = $item->pay_type == PayTypeEnum::SCORE ? UserWalletService::WALLET_TYPE_SCORE : UserWalletService::WALLET_TYPE_BALANCE;
  667. $insert_wallet [] = [
  668. 'mall_id' => $order['mall_id'],
  669. 'user_id' => $order['user_id'],
  670. 'is_frozen' => false,
  671. 'wallet_type' => $wallet_type,
  672. 'money' => $item->pay_fee,
  673. 'desc' => $desc,
  674. 'source_table' => LuckyGroupOrder::tableName(),
  675. 'source_table_id' => $order['id'],
  676. 'from_type' => LuckyGroupEnum::ADDONS_NAME,
  677. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  678. ];
  679. } elseif(in_array($item->pay_type,[PayTypeEnum::JOINPAY,PayTypeEnum::ALI,PayTypeEnum::JOINPAY_FAST_PAY,PayTypeEnum::WECHAT,PayTypeEnum::SANDPAY_FAST_PAY])) {
  680. // 退款原路退款
  681. $res = \Yii::$app->services->pay->refund(
  682. $item->pay_fee,
  683. $item->pay_fee,
  684. $payment_trade->out_trade_no,
  685. $order,
  686. '幸运拼团订单退款',
  687. '',
  688. '',
  689. $item->pay_type
  690. );
  691. if ($res === false) {
  692. \Yii::$app->custom->logs('幸运拼团综合订单退款失败', $order['order_no']);
  693. return true;
  694. };
  695. } elseif ($res = GlobalInvoke::exec(GlobalInvoke::INVOKE_ORDER_REFUND, $order['mall_id'], [
  696. 'id' => $refundModel->id,
  697. 'mall_id' => $refundModel->mall_id,
  698. 'refund' => $refundModel,
  699. 'params' => $params,
  700. 'order_no' => $order['order_no'],
  701. 'refund_order_no' => $refundModel->order_no,
  702. 'payment_trade_id' => $payment_trade->id,
  703. 'refund_price' => $item->pay_fee,
  704. 'order_type' => 'LuckyGroup',
  705. 'pay_type' => $item->pay_type,
  706. ])) {
  707. list($boolean, $msg) = $res;
  708. if (!$boolean) {
  709. \Yii::$app->custom->logs('幸运拼团退款订单退款失败', $msg);
  710. return true;
  711. }
  712. } else {
  713. \Yii::$app->custom->logs('幸运拼团退款订单退款失败', '不存在退款方式');
  714. return true;
  715. }
  716. }
  717. $payment_trade->setRefund($refundModel->id);
  718. LuckyGroupWinLog::setData(['mall_id'=>$order['mall_id'],'user_id'=>$order['user_id'],'activity_id'=>$order['activity_id'],'win_num'=>0]);
  719. $order->save();
  720. $transaction->commit();
  721. if (!empty($insert_wallet)) {
  722. UserWalletService::batchHandleByQueue($insert_wallet);
  723. }
  724. } catch (\Exception $e) {
  725. $transaction->rollBack();
  726. \Yii::$app->custom->logs('幸运拼团退款订单退款失败', $e->getMessage());
  727. }
  728. return true;
  729. }
  730. //创建主订单
  731. /**
  732. * @desc:生成主订单
  733. * @Author: hua
  734. * @Date: 2022/3/28
  735. * @Time: 10:47
  736. * @Copyright: copyright (c) 2021 广东七件事集团
  737. * @param $lucky_group_order
  738. * @param $pay_order_list
  739. * @param $pay_trade_list
  740. * @param $user_address_arr
  741. * @param $goods
  742. * @param $goods_attr
  743. * @return int
  744. * @throws Exception
  745. */
  746. public static function createMallOrder($lucky_group_order, $pay_order_list, $pay_trade_list, $user_address_arr, $goods, $goods_attr)
  747. {
  748. $t = \Yii::$app->db->beginTransaction();
  749. try {
  750. $new_goods_attr = GoodsAttr::findOne(['id' => $lucky_group_order->goods_attr_id]);
  751. $trade_id = $pay_order_list[$lucky_group_order['order_no']]['trade_id'];
  752. $out_trade_no = $pay_trade_list[$trade_id]['out_trade_no'];
  753. $user_address = $user_address_arr[$lucky_group_order['user_id']];
  754. $order = new Order();
  755. $order->loadDefaultValues();
  756. $order->mall_id = $lucky_group_order['mall_id'];
  757. $order->mch_id = $goods['mch_id'];
  758. $order->user_id = $lucky_group_order['user_id'];
  759. $order->order_no = StringHelper::generateOrderNo('s');
  760. $order->out_trade_no = $out_trade_no;
  761. $order->mobile = $user_address['mobile'] ?? '';
  762. if ($user_address) {
  763. $order->province = $user_address['province_id'];
  764. $order->city = $user_address['city_id'];
  765. $order->area = $user_address['district_id'];
  766. $order->town = $user_address['town_id'];
  767. $order->address = $user_address['detail'];
  768. $order->region_name = $user_address['province'] . $user_address['city'] . $user_address['district'] . $user_address['town'];
  769. $order->receiver_name = $user_address['name'];
  770. }
  771. $order->remark = $lucky_group_order['remark'];
  772. $order->shipping_type = ShippingTypeEnum::LOGISTICS;
  773. $order->shipping_money = $lucky_group_order['shipping_money'];
  774. $order->pay_money = $lucky_group_order['total_pay_price'];
  775. $order->goods_price = $lucky_group_order['total_goods_price'] / $lucky_group_order['buy_nums'];
  776. $order->original_goods_price = $lucky_group_order['total_goods_original_price'] / $lucky_group_order['buy_nums'];
  777. $order->order_status = OrderStatusEnum::PAY;
  778. $order->marketing_id = $lucky_group_order['id'];
  779. $order->marketing_type = LuckyGroupEnum::ADDONS_NAME;
  780. $order->pay_status = OrderStatusEnum::PAY;
  781. $order->extra_info = $lucky_group_order['major_data'];
  782. $order->payment_type = $lucky_group_order['payment_type'];
  783. $order->pay_time = $lucky_group_order['pay_at'];
  784. $order->score = 0;
  785. $res = $order->save();
  786. if ($res == false) throw new \Exception($order->getErrorMessage());
  787. $res = self::createMallOrderDetail($lucky_group_order, $order, $goods, $new_goods_attr);
  788. if ($res == false) throw new \Exception('创建主订单详情失败');
  789. if ($new_goods_attr->stock <= 0) throw new \Exception('库存不足');
  790. $new_goods_attr->stock--;
  791. $res = $new_goods_attr->save();
  792. if ($res == false) throw new \Exception($new_goods_attr->getErrorMessage());
  793. if ($goods->stock <= 0) throw new \Exception('库存不足');
  794. $goods->stock--;
  795. $goods->sales_num++;
  796. $res = $goods->save();
  797. if ($res == false) throw new \Exception($goods->getErrorMessage());
  798. $t->commit();
  799. return $order;
  800. } catch (\Exception $e) {
  801. $t->rollBack();
  802. var_dump($e->getMessage().$e->getFile().$e->getLine());
  803. return false;
  804. }
  805. }
  806. /**
  807. * @desc:生成主订单详情
  808. * @Author: hua
  809. * @Date: 2021/12/16
  810. * @Time: 14:32
  811. * @Copyright: copyright (c) 2021 广东七件事集团
  812. * @param $lucky_group_order
  813. * @param $order
  814. * @param $goods
  815. * @param $goods_attr
  816. * @param $marketing_type
  817. * @throws Exception
  818. */
  819. protected static function createMallOrderDetail($lucky_group_order, $order, $goods, $goods_attr)
  820. {
  821. // 因为主订单生成并且触发了订单支付事件。订单详情还没生成所以需要关联下
  822. try {
  823. // 订单详情
  824. $order_detail = new OrderDetail();
  825. $order_detail->loadDefaultValues();
  826. $order_detail->user_id = $lucky_group_order->user_id;
  827. $order_detail->mall_id = $lucky_group_order->mall_id;
  828. $order_detail->mch_id = $order->mch_id;
  829. $order_detail->order_id = $order->id;
  830. $order_detail->goods_id = $lucky_group_order->goods_id;
  831. $order_detail->goods_attr_id = $lucky_group_order->goods_attr_id;
  832. if ($goods) {
  833. $order_detail->goods_name = $goods->goods_name;
  834. //$order_detail->is_virtual = $goods->goods_type;
  835. $order_detail->is_virtual = $goods->goods_type == GoodsTypeEnum::GoodsTypeVirtual ? 1 : 0;
  836. }
  837. if ($goods_attr) {
  838. $order_detail->goods_attr_name = $goods_attr->name ?? '';
  839. $order_detail->price = $goods_attr->price;
  840. $order_detail->cost_price = $goods_attr->cost_price;
  841. $order_detail->original_goods_price = $goods_attr->original_price;
  842. $order_detail->pic_url = $goods_attr->pic_url ? $goods_attr->pic_url : $goods->cover_pic;
  843. }
  844. $order_detail->goods_price = $lucky_group_order['total_goods_price'] / $lucky_group_order['buy_nums'];
  845. $order_detail->goods_price = $lucky_group_order['total_goods_price'] / $lucky_group_order['buy_nums'];
  846. $order_detail->num = 1;
  847. $order_detail->marketing_id = $lucky_group_order->id;
  848. $order_detail->marketing_type = LuckyGroupEnum::ADDONS_NAME;
  849. $order_detail->order_status = OrderStatusEnum::PAY;
  850. $order_detail->remark = $lucky_group_order->remark;
  851. $res = $order_detail->save();
  852. if ($res == false) throw new \Exception('拼中生成订单详情失败');
  853. return $res;
  854. } catch (\Exception $e) {
  855. \Yii::$app->custom->logs('幸运拼团生成订单详情失败');
  856. return false;
  857. }
  858. }
  859. //获取上级数据
  860. protected static function getUserParentList($user_id_arr)
  861. {
  862. $user_relation_list = UserPartitionRelationship::lists(['where' => [['user_id' => $user_id_arr]], 'order' => 'part asc']);
  863. $data = [];
  864. foreach ($user_relation_list as $key => $value) {
  865. if (isset($data[$value['user_id']])) {
  866. $data[$value['user_id']] = array_merge($data[$value['user_id']], explode(',', $value['tree']));
  867. } else {
  868. $data[$value['user_id']] = explode(',', $value['tree']);
  869. }
  870. }
  871. return $data;
  872. }
  873. //获取等级数据
  874. protected static function getLevelConfig($activity)
  875. {
  876. $config_arr = [];
  877. $mall_id = $activity['mall_id'];
  878. if (!empty($activity['enable_commison'])) {
  879. $team_commission_setting = json_decode($activity['team_commission_setting'], true);
  880. $equal_commission_setting = json_decode($activity['equal_commission_setting'], true);
  881. $over_commission_setting = json_decode($activity['over_commission_setting'], true);
  882. foreach ($team_commission_setting as $item) {
  883. $config_arr[$item['level']]['is_percent_team'] = 0;
  884. $config_arr[$item['level']]['agent_team_prize'] = $item['team_commission'];
  885. }
  886. foreach ($equal_commission_setting as $item) {
  887. $config_arr[$item['level']]['is_percent_equal'] = 0;
  888. $config_arr[$item['level']]['agent_equal_prize'] = $item['equal_commission'];
  889. }
  890. foreach ($over_commission_setting as $item) {
  891. $config_arr[$item['level']]['is_percent_over'] = 0;
  892. $config_arr[$item['level']]['agent_over_prize'] = $item['over_commission'];
  893. }
  894. } else {
  895. $level_list = LuckyGroupLevel::lists(['where' => [['mall_id' => $mall_id], ['status' => StatusEnum::ENABLED]]]);
  896. foreach ($level_list as $item) {
  897. $config_arr[$item['level']]['is_percent_team'] = 0;
  898. $config_arr[$item['level']]['agent_team_prize'] = $item['team_commission'];
  899. $config_arr[$item['level']]['is_percent_equal'] = 0;
  900. $config_arr[$item['level']]['agent_equal_prize'] = $item['equal_commission'];
  901. $config_arr[$item['level']]['is_percent_over'] = 0;
  902. $config_arr[$item['level']]['agent_over_prize'] = $item['over_commission'];
  903. }
  904. }
  905. return $config_arr;
  906. }
  907. //禁用活动
  908. public function disableActivity($order_ids)
  909. {
  910. $joining_orders = LuckyGroupOrder::find()
  911. ->where(['id' => $order_ids, 'group_status' => 1])
  912. ->andWhere(new \yii\db\Expression('`buy_nums` > `win_nums`'))
  913. ->indexBy('id')
  914. ->all();
  915. foreach ($joining_orders as $key => $order) {
  916. $user_arr[$order['user_id']]['order_id'] = $order['id'];
  917. $order->group_status = 3;
  918. $this->refundToBalance($order, '禁用活动');
  919. }
  920. }
  921. }