RewardLogic.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. <?php
  2. namespace addons\WechatVideoShop\common\logic;
  3. use addons\WechatVideoShop\common\enums\WechatVideoShopEnums;
  4. use addons\WechatVideoShop\common\models\WechatVideoShopCommission;
  5. use addons\WechatVideoShop\common\models\WechatVideoShopGoodsSetting;
  6. use addons\WechatVideoShop\common\models\WechatVideoShopLevel;
  7. use addons\WechatVideoShop\common\models\WechatVideoShopOrder;
  8. use addons\WechatVideoShop\common\models\WechatVideoShopSharer;
  9. use common\enums\OrderStatusEnum;
  10. use common\enums\StatusEnum;
  11. use common\helpers\FormatHelper;
  12. use common\models\common\CapitalLog;
  13. use common\models\order\OrderDetail;
  14. use services\common\UserWalletService;
  15. use yii\helpers\Json;
  16. class RewardLogic
  17. {
  18. /**
  19. * 支付事件执行
  20. * @param array $order
  21. * @param array $config
  22. * @return bool
  23. */
  24. public static function reward(array $order, array $config)
  25. {
  26. try {
  27. $goods_ids = array_column($order['detail'], "goods_id");
  28. if (empty($goods_ids)) return true;
  29. $setting = WechatVideoShopGoodsSetting::lists([
  30. 'where' => [
  31. ['mall_id' => $order['mall_id']],
  32. ['status' => StatusEnum::ENABLED],
  33. // ['is_enable' => StatusEnum::ENABLED], // 必须是启用状态
  34. ['item_id' => $goods_ids]
  35. ],
  36. 'select'=> ['item_id', 'config', 'is_enable'],
  37. 'index' => 'item_id'
  38. ]);
  39. $wallet_insert = [];
  40. $commission_insert = [];
  41. $is_frozen = $config['settlement_type'] == OrderStatusEnum::ACCOMPLISH ? StatusEnum::ENABLED : StatusEnum::DISABLED;
  42. $score_name = \Yii::$app->services->setting->mall->get($order['mall_id'], "score.score_name");
  43. $score_name = !empty($score_name) ? $score_name : '积分';
  44. $wechat_video_shop_order = WechatVideoShopOrder::getOne([
  45. ['order_id' => $order['id']]
  46. ]);
  47. if (empty($wechat_video_shop_order->parent_id)) {
  48. // 没有上级可以进行分佣
  49. return true;
  50. }
  51. $sharer = $wechat_video_shop_order->sharer;
  52. if (empty($sharer)) {
  53. // 没有上级可以进行分佣
  54. return true;
  55. }
  56. // 当前用户等级
  57. $level = $sharer->level;
  58. $reward_user_id = $wechat_video_shop_order->parent_id;
  59. if (!empty($level)) { // 等级配置奖励
  60. $level_setting = WechatVideoShopLevel::getOne([
  61. ['level' => $level],
  62. ['mall_id' => $order['mall_id']],
  63. ['status' => StatusEnum::ENABLED]
  64. ]);
  65. }
  66. foreach ($order['detail'] as $item) {
  67. // 判断有没有启用
  68. $tmp_config = [
  69. "compute_type" => $config['compute_type'],
  70. "reward_commission_type" => $config['reward_commission_type'],
  71. "reward_commission" => $config['reward_commission'],
  72. "reward_score_type" => $config['reward_score_type'],
  73. "reward_score" => $config['reward_score'],
  74. ];
  75. if (!empty($level_setting)) { // 等级配置
  76. $tmp_config['reward_commission_type'] = $level_setting['reward_commission_type'];
  77. $tmp_config['reward_commission'] = $level_setting['reward_commission'];
  78. $tmp_config['reward_score_type'] = $level_setting['reward_score_type'];
  79. $tmp_config['reward_score'] = $level_setting['reward_score'];
  80. }
  81. if (!empty($setting[$item['goods_id']])) { // 独立设置 - 必须有等级,不然无法获取奖励金额 - 默认等级为0
  82. if (empty($setting[$item['goods_id']]['is_enable'])) continue; // 分佣没有启用
  83. // 遍历所有奖励,判断是否为独立设置
  84. $tmp_setting = Json::decode($setting[$item['goods_id']]['config']);
  85. if (!empty($tmp_setting['commission']['is_independent_setting'])) { // 独立设置
  86. $tmp_config['reward_commission_type'] = $tmp_setting['commission']['reward_type'] ?? $config['reward_commission_type'];
  87. $tmp_config['reward_commission'] = $tmp_setting['commission']['level'][$level] ?? $config['reward_commission'];
  88. }
  89. if (!empty($tmp_setting['score']['is_independent_setting'])) { // 独立设置
  90. $tmp_config['reward_score_type'] = $tmp_setting['score']['reward_type'] ?? $config['reward_score_type'];
  91. $tmp_config['reward_score'] = $tmp_setting['score']['level'][$level] ?? $config['reward_score'];
  92. }
  93. }
  94. if ($tmp_config['reward_commission'] > 0) {
  95. [$commission, $remark] = self::getCommission($tmp_config, $config, $item, UserWalletService::WALLET_TYPE_COMMISSION, "佣金");
  96. $wallet_insert[] = self::packWalletInsert($reward_user_id, $order, $item, $is_frozen,
  97. UserWalletService::WALLET_TYPE_COMMISSION, $commission,
  98. "分销员 [$reward_user_id] 推荐用户 [{$order['user_id']}] 从微信视频号小店下单 [{$order['order_no']}] 获得 $commission 佣金奖励",
  99. WechatVideoShopEnums::ADDONS_NAME, WechatVideoShopEnums::ADDONS_NAME);
  100. $commission_insert[] = self::packCommissionInsert($reward_user_id, $order, $item,
  101. WechatVideoShopEnums::REWARD_TYPE_COMMISSION, $commission, $is_frozen, $remark, $wechat_video_shop_order->shop_id);
  102. if (empty($is_frozen)) $sharer->total_commission += $commission;
  103. }
  104. if ($tmp_config['reward_score'] > 0) {
  105. [$commission, $remark] = self::getCommission($tmp_config, $config, $item, UserWalletService::WALLET_TYPE_SCORE, $score_name);
  106. $wallet_insert[] = self::packWalletInsert($reward_user_id, $order, $item, $is_frozen,
  107. UserWalletService::WALLET_TYPE_SCORE, $commission,
  108. "分销员 [$reward_user_id] 推荐用户 [{$order['user_id']}] 从微信视频号小店下单 [{$order['order_no']}] 获得 $commission $score_name 奖励",
  109. WechatVideoShopEnums::ADDONS_NAME, WechatVideoShopEnums::ADDONS_NAME);
  110. if (empty($is_frozen)) $sharer->total_score += $commission;
  111. $commission_insert[] = self::packCommissionInsert($reward_user_id, $order, $item,
  112. WechatVideoShopEnums::REWARD_TYPE_SCORE, $commission, $is_frozen, $remark, $wechat_video_shop_order->shop_id);
  113. }
  114. }
  115. $t = \Yii::$app->db->beginTransaction();
  116. if (!empty($commission_insert)) {
  117. // 写入插件记录
  118. WechatVideoShopCommission::find()->createCommand()
  119. ->batchInsert(WechatVideoShopCommission::tableName(),
  120. array_keys($commission_insert[0]), $commission_insert)->execute();
  121. // 给当前用户累加数据
  122. $sharer->total_order += 1; // 累加订单数量
  123. // 非冻结的才需要累加
  124. $sharer->total_sales += $order['pay_money']; // 累加销售金额
  125. $sharer->total_customers = WechatVideoShopOrder::find()
  126. ->where(['parent_id' => $sharer->user_id])
  127. ->andWhere(['is_pay' => StatusEnum::ENABLED])
  128. ->groupBy('user_id')->count();
  129. if (!$sharer->save()) throw new \Exception($sharer->getErrorMessage());
  130. }
  131. if (!empty($wallet_insert)) {
  132. UserWalletService::batchHandleByQueue($wallet_insert);
  133. if (!empty(UserWalletService::getStaticError())) throw new \Exception(UserWalletService::getStaticError());
  134. }
  135. $t->commit();
  136. } catch (\Exception $e) {
  137. if (!empty($t)) $t->rollBack();
  138. \Yii::error(FormatHelper::exception($e, __METHOD__));
  139. var_dump($e->getMessage(), $e->getLine(), $e->getFile());
  140. return false;
  141. }
  142. return true;
  143. }
  144. /**
  145. * @param int $user_id
  146. * @param array $order
  147. * @param array $goods
  148. * @param bool $is_frozen
  149. * @param string $wallet_type
  150. * @param $commission
  151. * @param string $desc
  152. * @param string $from_type
  153. * @param string $capital_type
  154. * @return array
  155. */
  156. protected static function packWalletInsert(int $user_id, array $order, array $goods, bool $is_frozen,
  157. string $wallet_type, $commission, string $desc, string $from_type,
  158. string $capital_type)
  159. {
  160. return [
  161. 'mall_id' => $order['mall_id'],
  162. 'user_id' => $user_id,
  163. 'is_frozen' => $is_frozen,
  164. 'wallet_type' => $wallet_type,
  165. 'money' => $commission,
  166. 'desc' => $desc,
  167. 'source_table' => 'order_detail',
  168. 'source_table_id' => $goods['id'],
  169. 'from_type' => $from_type,
  170. 'capital_type' => $capital_type,
  171. 'order_no' =>$order['order_no'],
  172. 'contributor_name' =>$goods['goods_name'],
  173. 'contributor_id' => $order['user_id'],
  174. 'contributor_money' =>$goods['goods_price'],
  175. ];
  176. }
  177. /**
  178. * @param int $user_id
  179. * @param array $order
  180. * @param array $goods
  181. * @param string $commission_type
  182. * @param $commission
  183. * @param int $settle_status
  184. * @param string $remark
  185. * @param int $shop_id
  186. * @return array
  187. */
  188. protected static function packCommissionInsert(int $user_id, array $order, array $goods,
  189. string $commission_type, $commission, int $settle_status,string $remark, int $shop_id)
  190. {
  191. return [
  192. 'mall_id' => $order['mall_id'],
  193. 'user_id' => $user_id,
  194. 'buy_user_id' => $order['user_id'],
  195. 'order_id' => $order['id'],
  196. 'order_no' => $order['order_no'],
  197. 'order_amount' => $order['pay_money'],
  198. 'remark' => $remark,
  199. 'settle_status' => $settle_status == StatusEnum::DISABLED ? WechatVideoShopEnums::SETTLE_STATUS_COMPLETE : WechatVideoShopEnums::SETTLE_STATUS_FREEZE,
  200. 'commission' => bcmul($commission, 100),
  201. 'commission_type' => $commission_type,
  202. 'goods_id' => $goods['id'],
  203. 'created_at' => time(),
  204. 'updated_at' => time(),
  205. 'shop_id' => $shop_id
  206. ];
  207. }
  208. /**
  209. * @param array $setting
  210. * @param array $config
  211. * @param array $goods
  212. * @param string $commission_type
  213. * @param string $commission_name
  214. * @return array
  215. */
  216. protected static function getCommission(array $setting, array $config, array $goods, string $commission_type, string $commission_name)
  217. {
  218. $commission = bcmul($setting["reward_{$commission_type}"], $goods['num'], 2);
  219. $remark = "固定";
  220. if ($setting["reward_{$commission_type}_type"] == StatusEnum::ENABLED) {
  221. $remark = "比例";
  222. }
  223. // 百分比
  224. $suffix = "";
  225. switch ($config['compute_type']) {
  226. case 0: // 实际支付金额
  227. if ($setting["reward_{$commission_type}_type"] == StatusEnum::ENABLED) {
  228. $commission = bcmul(bcmul($setting["reward_$commission_type"], $goods['goods_price'], 2), 0.01, 2);
  229. }
  230. $suffix = "(按实际支付金额计算)";
  231. break;
  232. case 1: // 商品售价
  233. if ($setting["reward_{$commission_type}_type"] == StatusEnum::ENABLED) {
  234. $commission = bcmul(bcmul($setting["reward_$commission_type"], bcmul($goods['price'], $goods['num'], 2), 2), 0.01, 2);
  235. }
  236. $suffix = "(按商品售价计算)";
  237. break;
  238. case 2: // 商品利润
  239. if ($setting["reward_{$commission_type}_type"] == StatusEnum::ENABLED) {
  240. $commission = bcmul(bcmul($setting["reward_$commission_type"], bcmul(bcsub($goods['price'], $goods['cost_price'], 2),
  241. $goods['num'], 2), 2), 0.01, 2);
  242. }
  243. $suffix = "(按商品利润计算)";
  244. break;
  245. }
  246. $remark .= "{$commission_name}[{$setting["reward_$commission_type"]}] $suffix";
  247. return [$commission, $remark];
  248. }
  249. /**
  250. * 完成事件执行
  251. * @param array $order
  252. * @param array $config
  253. * @return bool
  254. */
  255. public static function unfreeze(array $order, array $config)
  256. {
  257. $t = \Yii::$app->db->beginTransaction();
  258. try {
  259. $order_detail_list = $order['detail'] ?? [];
  260. if (empty($order_detail_list)) return true;
  261. $source_table_id_arr = array_column($order_detail_list, 'id');
  262. $capital_types = [
  263. WechatVideoShopEnums::ADDONS_NAME
  264. ];
  265. $where = [
  266. ['source_table' => 'order_detail'],
  267. ['source_table_id' => $source_table_id_arr],
  268. ['from_type' => WechatVideoShopEnums::ADDONS_NAME],
  269. ['capital_type' => $capital_types],
  270. ['status' => CapitalLog::STATUS_WAIT_SEND]
  271. ];
  272. $params['where'] = $where;
  273. //佣金
  274. $wallet_type = 'commission';
  275. CapitalLog::$capitalType = "{$wallet_type}_frozen";
  276. $commission_list = CapitalLog::lists($params);
  277. // 取出分享员
  278. if ($commission_list) {
  279. $sharer_user_id = $commission_list[0]['user_id'] ?? 0;
  280. $sharer = self::getSharer($sharer_user_id);
  281. foreach ($commission_list as $item) {
  282. $res = UserWalletService::unfrozen($item['id'], $wallet_type);
  283. if (!empty($sharer) && $res) {
  284. if ($item['change_type'] == 'add') $sharer->total_commission += $item['change_num'];
  285. } else {
  286. \Yii::error(__METHOD__ . " 分享员不存在 -> {$order['id']} -> {$sharer_user_id} -> {$res}");
  287. }
  288. }
  289. }
  290. //积分
  291. $wallet_type = 'score';
  292. CapitalLog::$capitalType = "{$wallet_type}_frozen";
  293. $score_list = CapitalLog::lists($params);
  294. if ($score_list) {
  295. if (empty($sharer)) {
  296. $sharer_user_id = $score_list[0]['user_id'] ?? 0;
  297. $sharer = self::getSharer($sharer_user_id);
  298. }
  299. foreach ($score_list as $item) {
  300. $res = UserWalletService::unfrozen($item['id'], $wallet_type);
  301. // change_num
  302. if (!empty($sharer) && $res) {
  303. if ($item['change_type'] == 'add') $sharer->total_commission += $item['change_num'];
  304. } else {
  305. \Yii::error(__METHOD__ . " 分享员不存在 -> {$order['id']} -> {$sharer_user_id} -> {$res}");
  306. }
  307. }
  308. }
  309. WechatVideoShopCommission::updateAll(['settle_status' => WechatVideoShopEnums::SETTLE_STATUS_COMPLETE], ['order_id' => $order['id']]);
  310. $t->commit();
  311. } catch (\Exception $e) {
  312. $t->rollBack();
  313. \Yii::error(FormatHelper::exception($e, __METHOD__));
  314. var_dump($e->getMessage());
  315. return false;
  316. }
  317. return true;
  318. }
  319. /**
  320. * 退款完成事件执行
  321. * @param array $refund
  322. * @param array $config
  323. * @return bool
  324. */
  325. public static function refund(array $refund, array $config)
  326. {
  327. $t = \Yii::$app->db->beginTransaction();
  328. try {
  329. $order_detail_id = $refund['order_detail_id'];
  330. $capital_types = [
  331. WechatVideoShopEnums::ADDONS_NAME
  332. ];
  333. $where = [
  334. ['source_table' => 'order_detail'],
  335. ['source_table_id' => $order_detail_id],
  336. ['from_type' => WechatVideoShopEnums::ADDONS_NAME],
  337. ['capital_type' => $capital_types],
  338. ['status' => CapitalLog::STATUS_WAIT_SEND]
  339. ];
  340. $params['where'] = $where;
  341. //佣金
  342. $wallet_type = 'commission';
  343. CapitalLog::$capitalType = "{$wallet_type}_frozen";
  344. $commission_list = CapitalLog::lists($params);
  345. if (!empty($commission_list)) { // 默认会删除?
  346. // 执行删除
  347. }
  348. $wallet_type = 'score';
  349. CapitalLog::$capitalType = "{$wallet_type}_frozen";
  350. $score_list = CapitalLog::lists($params);
  351. if (!empty($score_list)) {
  352. }
  353. $t->commit();
  354. } catch (\Exception $e) {
  355. $t->rollBack();
  356. \Yii::error(FormatHelper::exception($e, __METHOD__));
  357. return false;
  358. }
  359. return true;
  360. }
  361. /**
  362. * @param int $user_id
  363. * @return WechatVideoShopSharer|null
  364. */
  365. protected static function getSharer(int $user_id)
  366. {
  367. return WechatVideoShopSharer::getOne([
  368. ['user_id' => $user_id],
  369. ['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]],
  370. ]);
  371. }
  372. }