CommissionLogLogic.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <?php
  2. namespace addons\Boss\common\logic;
  3. use addons\Boss\common\enums\BossEnum;
  4. use addons\Boss\common\enums\LevelEnum;
  5. use addons\Boss\common\models\AddonsBoss;
  6. use addons\Boss\common\models\AddonsBossCommissionJob;
  7. use addons\Boss\common\models\AddonsBossCommissionLog;
  8. use addons\Boss\common\models\AddonsBossLevelSetting;
  9. use addons\Boss\common\models\AddonsBossRewardLog;
  10. use addons\Boss\common\models\BossPoolPrizeJob;
  11. use common\enums\StatusEnum;
  12. use common\enums\UserWalletEnum;
  13. use common\helpers\FileHelper;
  14. use common\helpers\FormatHelper;
  15. use common\models\mall\Mall;
  16. use common\models\user\UserDigital;
  17. use common\traits\ErrorTrait;
  18. use Exception;
  19. use services\common\UserWalletService;
  20. use Yii;
  21. class CommissionLogLogic{
  22. use ErrorTrait;
  23. /**
  24. * 执行分红操作
  25. * @Author: lun
  26. * @DateTime: 2021/11/5 0005 14:27
  27. * @Copyright: copyright (c) 2021 广东七件事集团
  28. * @return bool
  29. */
  30. public function execute()
  31. {
  32. $mall_list = Mall::getEnableMallList(true);
  33. if(!empty($mall_list)){
  34. foreach ($mall_list as $mall){
  35. $mallId = $mall['id'];
  36. // 获取基础配置
  37. $config = Yii::$app->services->setting->addons->get($mallId, 'Boss', 'basic');
  38. if ($config['is_enable'] == StatusEnum::ENABLED) {
  39. $continue = false;
  40. // 获取结算周期,0=天,1=周,2=月
  41. $compute_period = $config['compute_period'];
  42. switch ($config['compute_period']) {
  43. case 1:
  44. $start_day = date("Ymd", strtotime("-1 day"));
  45. $end_day = $start_day;
  46. break;
  47. case 2:
  48. if (date('w') != 1) $continue = true;// 非周一直接退出
  49. $start_day = date("Ymd", strtotime("last Monday"));
  50. $end_day = date('Ymd', strtotime('-1 sunday', time()));;
  51. break;
  52. case 3:
  53. if (date('d') != 1) $continue = true;// 非当月1号直接退出
  54. $start_day = date("Ym01", strtotime("-1 month"));
  55. $end_day = date('Ymd', strtotime(date('Y-m', time()) . '-01 00:00:00') - 86400);
  56. break;
  57. case LevelEnum::PERIOD_CUSTOM_DAYS:
  58. // 没配置直接退出
  59. if (empty($config['custom_compute_days'])) {
  60. $continue = true;
  61. break;
  62. }
  63. // 自定义天数
  64. $custom_compute_days = (int)$config['custom_compute_days'];
  65. $latestJobDateLimit = AddonsBossCommissionJob::find()
  66. ->where(['mall_id' => $mallId, 'compute_period' => $compute_period, 'status' => StatusEnum::ENABLED])
  67. ->orderBy('id desc')
  68. ->select('date_limit')
  69. ->scalar();
  70. // 得到本次开始时间
  71. $start_day = date("Ymd", strtotime("-$custom_compute_days day"));
  72. // 包含在这个时间则直接退出
  73. if ($latestJobDateLimit) {
  74. [, $latestEndDay] = explode('-', $latestJobDateLimit);
  75. if ($latestEndDay >= $start_day) {
  76. $continue = true;
  77. break;
  78. }
  79. }
  80. $end_day = date("Ymd", strtotime("-1 day"));
  81. break;
  82. }
  83. if ($continue) continue;
  84. $date_limit = $start_day . '-' . $end_day;
  85. $job = AddonsBossCommissionJob::findOne(['mall_id' => $mallId, 'compute_period' => $compute_period, 'date_limit' => $date_limit, 'status' => StatusEnum::ENABLED]);
  86. if ($job) continue;
  87. // 生成执行记录
  88. $job = new AddonsBossCommissionJob();
  89. $job->mall_id = $mallId;
  90. $job->date_limit = $date_limit;
  91. $job->compute_period = $compute_period;
  92. if (!$job->save()) throw new Exception($job->getErrorMessage());
  93. $res = $this->addCommissionLog($mallId, $config, $start_day, $end_day, $job->id);
  94. // 发放股东团队业绩奖励
  95. $bossPerformanceLogic = new BossPerformanceLogic();
  96. $bossPerformanceLogic->sendPerformancePrize($mallId, $config, $start_day, $end_day, $job->id);
  97. //if($res === false) throw new Exception($this->getError());
  98. }
  99. }
  100. foreach ($mall_list as $mall){
  101. $mallId = $mall['id'];
  102. // 获取基础配置
  103. $config = Yii::$app->services->setting->addons->get($mallId, 'Boss', 'basic');
  104. // 奖金池
  105. if($config['is_enable'] == StatusEnum::ENABLED && ($config['is_prize_pool'] ?? StatusEnum::DISABLED) == StatusEnum::ENABLED){
  106. list(
  107. 'continue' => $continue,
  108. 'start_day' => $start_day,
  109. 'end_day' => $end_day,
  110. 'compute_period' => $compute_period
  111. ) = $this->getExecutePeriod($config, 'prize_pool_period');
  112. if ($continue) continue;
  113. $date_limit = $start_day . '-' . $end_day;
  114. $date_limit = $start_day . '-' . $end_day;
  115. $job = BossPoolPrizeJob::findOne(['mall_id' => $mallId, 'compute_period' => $compute_period, 'date_limit' => $date_limit, 'status' => StatusEnum::ENABLED]);
  116. if ($job) continue;
  117. // 生成执行记录
  118. $job = new BossPoolPrizeJob();
  119. $job->mall_id = $mallId;
  120. $job->date_limit = $date_limit;
  121. $job->compute_period = $compute_period;
  122. $last_period = BossPoolPrizeJob::find()->where(['mall_id' => $mallId, 'status' => StatusEnum::ENABLED])
  123. ->select('period')->orderBy('id DESC')->scalar();
  124. $period = $last_period ? $last_period + 1 : 1;
  125. $job->period = $period;
  126. if (!$job->save()) throw new Exception($job->getErrorMessage());
  127. $poolPrizeLogic = new PoolPrizeLogic();
  128. $poolPrizeLogic->send($mallId, $config, $start_day, $end_day, $job->id);
  129. }
  130. }
  131. return true;
  132. }
  133. }
  134. protected function getExecutePeriod($config, $key)
  135. {
  136. $continue = false;
  137. $start_day = 0;
  138. $end_day = 0;
  139. // 获取结算周期,0=天,1=周,2=月
  140. $compute_period = $config[$key];
  141. switch ($config[$key]) {
  142. case 1:
  143. $start_day = date("Ymd", strtotime("-1 day"));
  144. // $start_day = date("Ymd");
  145. $end_day = $start_day;
  146. break;
  147. case 2:
  148. if (date('w') != 1) $continue = true; // 非周一直接退出
  149. $start_day = date("Ymd", strtotime("last Monday"));
  150. $end_day = date('Ymd', strtotime('-1 sunday', time()));;
  151. break;
  152. case 3:
  153. if (date('d') != 1) $continue = true; // 非当月1号直接退出
  154. $start_day = date("Ym01", strtotime("-1 month"));
  155. $end_day = date('Ymd', strtotime(date('Y-m', time()) . '-01 00:00:00') - 86400);
  156. break;
  157. }
  158. return compact('continue', 'start_day', 'end_day', 'compute_period');
  159. }
  160. /**
  161. * 增加boss分红佣金
  162. * @Author: lun
  163. * @DateTime: 2021/11/5 0005 14:27
  164. * @Copyright: copyright (c) 2021 广东七件事集团
  165. * @param $mallId
  166. * @param $config
  167. * @param $start_day
  168. * @param $end_time
  169. * @return bool
  170. */
  171. public function addCommissionLog($mallId, $config, $start_day, $end_day, $job_id = 0)
  172. {
  173. if ($config['is_enable'] == StatusEnum::DISABLED) {
  174. Yii::error('系统没有开启股东提成');
  175. return true;
  176. }
  177. $trans = Yii::$app->db->beginTransaction();
  178. try {
  179. // 初始化数据
  180. $time = time();
  181. $table_name = 'addons_boss_commission_log';
  182. $reward = [];
  183. // 获取等级设置
  184. $setting = AddonsBossLevelSetting::find()->where(['mall_id' => $mallId, 'status' => StatusEnum::ENABLED])->indexBy('level')->asArray()->all();
  185. foreach ($setting as $level => &$item) {
  186. $item['extra_restrict_keys'] = json_decode($item['extra_restrict_keys'], true) ?? [];
  187. $item['extra_restrict_values'] = json_decode($item['extra_restrict_values'], true) ?? [];
  188. }
  189. // 检查是否开启沉淀池,并且比例不为零
  190. $sediment_percent = !empty($config['is_sediment']) ? $config['sediment_percent'] ?? 0 : 0;
  191. $besides_price_type = [LevelEnum::DIGITAL_PRIZE, LevelEnum::EXTRA_DIGITAL_PRIZE];
  192. // 获取分红奖励列表
  193. $i = 1;
  194. $page_size = 1000;
  195. while ($reward_list = AddonsBossRewardLog::find()->select('user_id,sum(amounts) amounts,sum(commission) commission,type,from_type')
  196. ->where(['mall_id' => $mallId, 'is_settlement' => StatusEnum::DISABLED])
  197. ->andFilterWhere(['between', 'date', $start_day, $end_day])
  198. ->groupBy('user_id,type')
  199. ->offset(($i - 1) * $page_size)->limit($page_size)->asArray()->all()) {
  200. // 获取股东信息
  201. $boss_uids = array_unique(array_column($reward_list, 'user_id'));
  202. $boss_arr = AddonsBoss::find()->select('user_id,level,created_at')->where(['user_id' => $boss_uids, 'status' => StatusEnum::ENABLED])->indexBy('user_id')->asArray()->all();
  203. // 生成计算好的结算数据
  204. foreach ($reward_list as $item) {
  205. if (empty($boss_arr[$item['user_id']])) continue;
  206. $user_level = $boss_arr[$item['user_id']]['level'];// 股东当前等级
  207. if(!in_array($item['type'], $besides_price_type)){
  208. if ($item['type'] == 2 && !empty($setting[$user_level]['extra_restrict']) && !empty($setting[$user_level]['extra_restrict_keys'])) {
  209. // 符合条件三的用户
  210. if (in_array(2, $setting[$user_level]['extra_restrict_keys'])) {
  211. $item['commission'] = $this->restrictCond3($boss_arr[$item['user_id']], $setting[$user_level]['extra_restrict_values'][2]['value'], $time, $item['commission']);
  212. }
  213. }
  214. // 增加沉淀池数据
  215. if ($sediment_percent) {
  216. // sediment_type[1=永久分红,2=额外分红,3=永久+额外分红]
  217. if ($config['sediment_type'] == 1 && $item['type'] == 1 || $config['sediment_type'] == 2 && $item['type'] == 2 || $config['sediment_type'] == 3) {
  218. $item['level'] = $user_level;
  219. $sediment = AddonsBossRewardLog::createSediment($mallId, $item, $sediment_percent, $job_id);
  220. $item['commission'] = $sediment['commission'];
  221. $item['sedimen_money'] = $sediment['sedimen_money'];
  222. !empty($sediment['reward']) && $reward[] = $sediment['reward'];
  223. }
  224. }
  225. }
  226. if ($item['commission'] < 0.01) continue;// 小于一分钱不允许插入数据
  227. $rows[] = [
  228. 'mall_id' => $mallId,
  229. 'user_id' => $item['user_id'],
  230. 'amounts' => $item['amounts'],
  231. 'commission' => $item['commission'],
  232. 'sedimen_money' => $item['sedimen_money'] ?? 0,
  233. 'type' => $item['type'],
  234. 'compute_period' => $config['compute_period'],
  235. 'start_time' => $start_day,
  236. 'end_time' => $end_day,
  237. 'created_at' => $time,
  238. 'updated_at' => $time,
  239. 'from_type' => $item['from_type'],
  240. ];
  241. }
  242. $i++;
  243. }
  244. if (empty($rows)) throw new Exception('佣金记录为空');
  245. // 生成数据
  246. $field = ['mall_id','user_id','amounts','commission','sedimen_money','type','compute_period','start_time','end_time','created_at','updated_at', 'from_type'];
  247. $res = Yii::$app->db->createCommand()->batchInsert(AddonsBossCommissionLog::tableName(), $field, $rows)->execute();
  248. if (!$res) throw new Exception('批量生成分红数据失败');
  249. // 数据生成完成记录表修改结算状态
  250. AddonsBossRewardLog::updateAll(
  251. ['is_settlement' => StatusEnum::ENABLED, 'updated_at' => $time],
  252. ['and', ['mall_id' => $mallId, 'is_settlement' => StatusEnum::DISABLED],['between', 'date', $start_day, $end_day]]
  253. );
  254. // 生成沉淀池数据
  255. if ($reward) {
  256. $field = ['mall_id','user_id','type','level','amounts','commission','settlement_config','settlement_data','is_settlement','date','created_at','updated_at'];
  257. Yii::$app->db->createCommand()->batchInsert(AddonsBossRewardLog::tableName(), $field, $reward)->execute();
  258. }
  259. $digital_arr = [];
  260. $k = 1;
  261. $page_log = 1000;
  262. // 增加对应股东分佣金额
  263. while ($log_list = AddonsBossCommissionLog::find()->where(['mall_id' => $mallId,'start_time' => $start_day,'end_time' => $end_day])->andWhere(['!=', 'type', LevelEnum::PERFORMANCE_PRIZE])
  264. ->offset(($k - 1) * $page_log)->limit($page_log)->asArray()->all()) {
  265. foreach ($log_list as $item) {
  266. if(in_array($item['type'], $besides_price_type)){
  267. $desc = $item['type'] == LevelEnum::DIGITAL_PRIZE ? '股东管理数字币永久分红' : '股东管理额外分红数字币';
  268. $digital_arr[] = [
  269. 'mall_id' => $mallId,
  270. 'user_id' => $item['user_id'],
  271. 'change_num' => $item['commission'],
  272. 'is_frozen' => false,
  273. 'params' => [
  274. 'desc' => $desc,
  275. 'source_table' => $table_name,
  276. 'source_table_id' => $item['id'],
  277. 'from_type' => BossEnum::ADDONS_NAME,
  278. 'capital_type' => BossEnum::ADDONS_NAME,
  279. 'order_no' => ''
  280. ]
  281. ];
  282. if ($item['type'] == LevelEnum::DIGITAL_PRIZE) {
  283. $boss_commission[$item['user_id']]['digital'] = $item['commission'];
  284. } else {
  285. $boss_commission[$item['user_id']]['extra_digital'] = $item['commission'];
  286. }
  287. }else{
  288. $insert_wallet[] = [
  289. 'mall_id' => $mallId,
  290. 'user_id' => $item['user_id'],
  291. 'is_frozen' => false,
  292. 'wallet_type' => 'commission',
  293. 'money' => $item['commission'],
  294. 'desc' => '股东分红佣金',
  295. 'source_table' => $table_name,
  296. 'source_table_id' => $item['id'],
  297. 'from_type' => BossEnum::ADDONS_NAME,
  298. 'capital_type' => $item['type'] == 1 ? UserWalletEnum::FOREVER : UserWalletEnum::BO_EXTRA,
  299. ];
  300. if ($item['type'] == 1) {
  301. $boss_commission[$item['user_id']]['commission'] = $item['commission'];
  302. } else {
  303. $boss_commission[$item['user_id']]['extra_commission'] = $item['commission'];
  304. }
  305. }
  306. }
  307. $k++;
  308. }
  309. // 增加用户股东金额
  310. foreach ($boss_commission as $user_id => $commission) {
  311. $boss = AddonsBoss::findOne(['mall_id' => $mallId, 'user_id' => $user_id, 'status' => StatusEnum::ENABLED]);
  312. $boss->commission = empty($commission['commission']) ? $boss->commission : $boss->commission + $commission['commission'];
  313. $boss->extra_commission = empty($commission['extra_commission']) ? $boss->extra_commission : $boss->extra_commission + $commission['extra_commission'];
  314. $boss->total_commission = $boss->commission + $boss->extra_commission + $boss->performance_commission;
  315. $boss->digital = empty($commission['digital']) ? $boss->digital : $boss->digital + $commission['digital'];
  316. $boss->extra_digital = empty($commission['extra_digital']) ? $boss->extra_digital : $boss->extra_digital + $commission['extra_digital'];
  317. $boss->total_digital = $boss->digital + $boss->extra_digital;
  318. if (!$boss->save()) throw new \Exception($boss->getErrorMessage());
  319. }
  320. $trans->commit();
  321. if ($insert_wallet) {
  322. UserWalletService::batchHandleByQueue($insert_wallet);
  323. }
  324. if($digital_arr){
  325. foreach($digital_arr as $val){
  326. $res = UserDigital::handleUserDigital($val['mall_id'], $val['user_id'], $val['change_num'], $val['is_frozen'], $val['params']);
  327. if ($res === false) {
  328. \Yii::error(__METHOD__. '//股东定时分红赠送数字币,err:' . UserDigital::getStaticError());
  329. }
  330. }
  331. }
  332. return true;
  333. } catch (Exception $e) {
  334. $trans->rollBack();
  335. $exception = FormatHelper::exception($e, "mall_id={$mallId} 股东定时分红任务失败");
  336. Yii::error($exception);
  337. $this->setError($exception);
  338. return false;
  339. }
  340. }
  341. /**
  342. * desc@股东额外分红限制条件3
  343. * __天内最多可获得__元额外分红
  344. * @Author: lun
  345. * @DateTime: 2022/9/13 0013 14:39
  346. * @Copyright: copyright (c) 2021 广东七件事集团
  347. * @param $boss
  348. * @param $setting
  349. * @param $time
  350. * @param int $available_commission
  351. * @return int
  352. */
  353. public function restrictCond3($boss, $setting, $time, $available_commission = 0)
  354. {
  355. $user_id = $boss['user_id'];
  356. // 获取N天的时间戳 - 增加300秒作为误差值
  357. $end_time = $time - (86400 * $setting['day'] + 300);
  358. if ($boss['created_at'] < $end_time) return 0;// 超过N天直接没有额外收入
  359. $original_commission = $available_commission;
  360. // 计算N天内已获得的额外分红金额 - 增加300秒作为误差值
  361. $extra_commission = AddonsBossCommissionLog::find()->where(['user_id' => $user_id, 'type' => 2, 'status' => StatusEnum::ENABLED])
  362. ->andWhere(['>=', 'created_at', $end_time])->sum('commission') ?? 0;
  363. // 如果N天内已获得的金额大于或等于设置金额则不再获得金额
  364. if ($extra_commission >= $setting['money']) {
  365. $available_commission = 0;
  366. } else {
  367. $surplus_extra = $setting['money'] - $extra_commission;// 剩余可得额外分红
  368. // 如果本次结算额外分红大于剩余可得额外分红,则最多只能获取到剩余的分红
  369. if ($available_commission >= $surplus_extra) $available_commission = $surplus_extra;
  370. }
  371. // 将不发送额外分红的用户数据存入日志,便于查询
  372. if ($original_commission > $available_commission) {
  373. FileHelper::writeLog($this->getLogPath('restrictCond3'), "user_id={$user_id} 本次原额外分红={$original_commission},extra_commission={$extra_commission},set_money={$setting['money']},只得到{$available_commission}元额外分红");
  374. }
  375. return $available_commission;
  376. }
  377. protected function getLogPath($type)
  378. {
  379. return Yii::getAlias('@runtime') . "/logs/boss/" . $type . '/' . date('Y_m_d') . '.txt';
  380. }
  381. }