CalcServeBonusCalc.php 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: leo
  5. * Date: 2018/8/2
  6. * Time: 上午10:32
  7. */
  8. namespace common\helpers\bonus;
  9. use common\helpers\Cache;
  10. use common\helpers\Date;
  11. use common\helpers\snowflake\SnowFake;
  12. use common\helpers\Tool;
  13. use common\models\BsBonus103Calc;
  14. use common\models\BsBonus103Calc1;
  15. use common\models\BsBonus103CalcNet;
  16. use common\models\BsBonus103CalcNet1;
  17. use common\models\BsBonus103Test;
  18. use common\models\BsBonus103TestNet;
  19. use common\models\CalcBonus;
  20. use common\models\CalcBonusBD;
  21. use common\models\CalcBonusBS;
  22. use common\models\CalcBonusBsCalc;
  23. use common\models\CalcBonusBsChecking;
  24. use common\models\CalcBonusBsDetail;
  25. use common\models\CalcBonusBsDetailBonus;
  26. use common\models\CalcBonusBsDetailCalc;
  27. use common\models\CalcBonusBsDetailGpv;
  28. use common\models\CalcBonusBsStat;
  29. use common\models\CalcBonusQuarter;
  30. use common\models\CalcBonusGarage;
  31. use common\models\CalcBonusQY;
  32. use common\models\CalcBonusTG;
  33. use common\models\CalcBonusTourism;
  34. use common\models\CalcBonusVilla;
  35. use common\models\Config;
  36. use common\models\DeclarationLevel;
  37. use common\models\EmployLevel;
  38. use common\models\PerfMonth;
  39. use common\models\PerfPeriod;
  40. use common\models\Period;
  41. use common\models\QtrCalcRecord;
  42. use common\models\QtrCalcScore;
  43. use common\models\QtrCalcUser;
  44. use common\models\QtrCalcWeight;
  45. use common\models\ServeProcess;
  46. use common\models\StarCrownLevel;
  47. use Exception;
  48. use Yii;
  49. use yii\base\BaseObject;
  50. use yii\base\StaticInstanceTrait;
  51. use yii\db\Query;
  52. class CalcServeBonusCalc extends BaseObject {
  53. use StaticInstanceTrait;
  54. private $_limit = 3000;
  55. private $_handleUserId;
  56. private $_sysConfig = [];
  57. private $_decLevelConfig = [];
  58. private $_empLevelConfig = [];
  59. private $_starCrownLevelConfig = [];
  60. private $_decRoleConfig = [];
  61. private $_errors = [];
  62. private $_periodNum = 0;
  63. private $_periodId;
  64. private $_isCalcMonth = 0;
  65. private $_calcYear;
  66. private $_calcMonth;
  67. private $_calcYearMonth;
  68. private $_calcMonthPeriodNumCount = 0;
  69. private $_isPerpare;
  70. //pv
  71. private $_pvRatio;
  72. private $_calcZone = ['openTravel', 'openCar', 'openHouse'];
  73. const LOOP_FINISH = 1;
  74. const LOOP_CONTINUE = 2;
  75. const ORDER_TYPE_TO_FW_COEFFICIENT = [
  76. 'ZC' => 'fwCoefficientFromZc',
  77. 'FX_CASH' => 'fwCoefficientFromFxCash',
  78. 'FX_POINT' => 'fwCoefficientFromFxPoint',
  79. ];
  80. //最小报单pv
  81. const MIN_BD_PV = 980;
  82. public function init() {
  83. parent::init();
  84. }
  85. /**
  86. * 设置期数
  87. * @param int $periodNum
  88. * @return int
  89. */
  90. public function setPeriodNum(int $periodNum) {
  91. return $this->_periodNum = $periodNum;
  92. }
  93. /**
  94. * 获取期数
  95. * @return int
  96. */
  97. public function getPeriodNum() {
  98. return $this->_periodNum;
  99. }
  100. /**
  101. * 加入错误错误
  102. * @param $attr
  103. * @param $error
  104. */
  105. public function addError($attr, $error) {
  106. $this->_errors[$attr][] = $error;
  107. }
  108. /**
  109. * 获取错误信息
  110. * @return array
  111. */
  112. public function getErrors() {
  113. return $this->_errors;
  114. }
  115. /**
  116. * 开始执行结算步骤
  117. * @param $periodNum
  118. * @param null $handleUserId
  119. * @return bool
  120. */
  121. public function calcStep() {
  122. try {
  123. $this->_errors = [];
  124. $this->setPeriodNum(0);
  125. $this->_handleUserId = '';
  126. $t1 = microtime(true);
  127. // 初始化结算任务
  128. $this->initCalcTask();
  129. // 判断是否能开始进行计算奖金
  130. $checkStart = $this->checkStart();
  131. if (!$checkStart) {
  132. return false;
  133. } else {
  134. // 更新状态为,奖金计算中
  135. Period::updateCalcProcess(4, $this->_periodNum);
  136. }
  137. $t2 = microtime(true);
  138. ServeProcess::recordProcess($t1, $t2, $this->_periodNum, '奖金计算初始化配置', 'bonus');
  139. // 设置结算状态
  140. $this->setCalcStatus('start');
  141. // 清空所有本期结算用到的缓存
  142. CalcCache::clearCalcBonusCache($this->_periodNum);
  143. $t3 = microtime(true);
  144. ServeProcess::recordProcess($t2, $t3, $this->_periodNum, '设置结算状态,清空缓存', 'bonus');
  145. // 清空相关表数据
  146. $this->clearCalcTableData();
  147. $t4 = microtime(true);
  148. ServeProcess::recordProcess($t3, $t4, $this->_periodNum, '清空相关表数据', 'bonus');
  149. echo('初始化、清空缓存及相关数据表完成,耗时:' . round($t4 - $t1, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  150. $this->_updatePercent(10);
  151. // 蓝星奖放到最前面 奖金计算开始
  152. if($this->_sysConfig['openGL']['VALUE']) {
  153. echo('计算蓝星奖开始,' . date('Y-m-d H:i:s', $t4) . PHP_EOL);
  154. // 调用存储过程,计算蓝星管理奖金
  155. $this->calcBsProcedure();
  156. // 将有蓝星管理奖金的用户加入到有奖金缓存用户中
  157. $this->calcBonusBsGL();
  158. if ($this->_isCalcMonth) {
  159. ServeProcess::recordProcess($t4, time(), $this->_periodNum, '计算蓝星奖', 'bonus');
  160. }
  161. }
  162. $t5 = microtime(true);
  163. echo('计算蓝星奖'.($this->_sysConfig['openGL']['VALUE']?'完成':'关闭').',耗时:' . round($t5 - $t4, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  164. // 更新期业绩表中用户的聘级
  165. if ($this->_isCalcMonth) {
  166. $this->loopUpdatePeriod();
  167. }
  168. // 更新月业绩表中用户的最新聘级
  169. if ($this->_isCalcMonth) {
  170. $this->loopUpdateMonthPerf();
  171. }
  172. if($this->_sysConfig['openFW']['VALUE']) {
  173. $this->calcBonusBDStepOne();
  174. $this->calcBonusBDStepTwo();
  175. ServeProcess::recordProcess($t5, time(), $this->_periodNum, '计算服务奖', 'bonus');
  176. }
  177. $t6 = microtime(true);
  178. echo('计算服务奖'.($this->_sysConfig['openFW']['VALUE']?'完成':'关闭').',耗时:' . round($t6 - $t5, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  179. $this->_updatePercent(15);
  180. // 销售奖/推广奖
  181. if($this->_sysConfig['openTG']['VALUE']) {
  182. $this->calcBonusTG();
  183. ServeProcess::recordProcess($t6, time(), $this->_periodNum, '计算推广奖', 'bonus');
  184. }
  185. $t7 = microtime(true);
  186. echo('计算推广奖'.($this->_sysConfig['openTG']['VALUE']?'完成':'关闭').',耗时:' . round($t7 - $t6, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  187. $this->_updatePercent(20);
  188. // 绩效奖/团队奖
  189. if($this->_sysConfig['openQY']['VALUE']) {
  190. $this->calcBonusQY();
  191. ServeProcess::recordProcess($t7, time(), $this->_periodNum, '计算团队奖', 'bonus');
  192. }
  193. $t8 = microtime(true);
  194. echo('计算团队奖'.($this->_sysConfig['openQY']['VALUE']?'完成':'关闭').',耗时:' . round($t8 - $t7, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  195. $this->_updatePercent(35);
  196. // $this->calcBonusTourism($this->_sysConfig['openTourism']);
  197. // $t21 = microtime(true);
  198. // echo('计算旅游奖' . ($this->_sysConfig['openTourism']['VALUE'] ? '完成' : '关闭') . ',耗时:' . round($t21 - $t20, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  199. // $this->_updatePercent(68);
  200. if($this->_sysConfig['openVilla']['VALUE']) {
  201. $this->calcBonusVilla();
  202. ServeProcess::recordProcess($t8, time(), $this->_periodNum, '计算房奖', 'bonus');
  203. }
  204. $t22 = microtime(true);
  205. echo('计算房奖' . ($this->_sysConfig['openVilla']['VALUE'] ? '完成' : '关闭').',耗时:' . round($t22 - $t8, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  206. $this->_updatePercent(45);
  207. if($this->_sysConfig['openGarage']['VALUE']) {
  208. $this->calcBonusGarage();
  209. ServeProcess::recordProcess($t22, time(), $this->_periodNum, '计算车奖', 'bonus');
  210. }
  211. $t23 = microtime(true);
  212. echo('计算车奖' . ($this->_sysConfig['openGarage']['VALUE'] ? '完成' : '关闭').',耗时:' . round($t23 - $t22, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  213. $this->_updatePercent(55);
  214. // 计算季度奖
  215. if($this->_sysConfig['openQuarter']['VALUE']) {
  216. $this->calcQuarter();
  217. ServeProcess::recordProcess($t23, time(), $this->_periodNum, '计算季度奖-调用存储过程', 'bonus');
  218. }
  219. $t24 = microtime(true);
  220. echo('计算季度奖' . ($this->_sysConfig['openQuarter']['VALUE'] ? '开启调用存储过程' : '关闭').',耗时:' . round($t24 - $t23, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  221. // 将用户写入缓存
  222. if($this->_sysConfig['openQuarter']['VALUE']) {
  223. $this->calcQuarterUser();
  224. ServeProcess::recordProcess($t24, time(), $this->_periodNum, '计算季度奖-存入奖金会员', 'bonus');
  225. }
  226. $this->_updatePercent(65);
  227. $t25 = microtime(true);
  228. echo('计算季度奖' . ($this->_sysConfig['openQuarter']['VALUE'] ? '完成' : '关闭').',耗时:' . round($t25 - $t24, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  229. // 奖金写库
  230. $this->loopBonusUsers();
  231. $this->_updatePercent(75);
  232. $t30 = microtime(true);
  233. ServeProcess::recordProcess($t25, $t30, $this->_periodNum, '奖金写库', 'bonus');
  234. echo('奖金写库操作完成,耗时:' . round($t30 - $t25, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  235. ServeProcess::recordProcess($t30, time(), $this->_periodNum, '标记为计算完成', 'bonus');
  236. $this->endCalcTask();
  237. $this->_updatePercent(100);
  238. $t35 = microtime(true);
  239. echo('结算全部完成,共耗时:' . round($t35 - $t1, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  240. } catch (\Exception $e) {
  241. $this->errorCalcTask();
  242. $this->addError('calc', sprintf('File【%s】, Line【%s】, Msg【%s】', $e->getFile(), $e->getLine(), $e->getMessage()));
  243. return false;
  244. }
  245. return true;
  246. }
  247. /**
  248. * 结算完成
  249. */
  250. public function endCalcTask() {
  251. $this->setCalcStatus('end');// 更新结算状态
  252. }
  253. /**
  254. * 结算错误
  255. */
  256. public function errorCalcTask() {
  257. CalcCache::clearCalcBonusCache($this->_periodNum); // 清空所有本期结算用到的缓存
  258. $this->setCalcStatus('fail'); // 更新结算状态
  259. }
  260. /**
  261. * 初始化结算任务
  262. * @throws \yii\db\Exception
  263. */
  264. public function initCalcTask() {
  265. $periodObj = Period::instance();
  266. $periodDataArr = $periodObj->setPeriodNum($this->_periodNum);
  267. if (empty($this->_periodNum)) {
  268. $this->_periodNum = $periodDataArr['PERIOD_NUM'];
  269. }
  270. $this->_sysConfig = Cache::getSystemConfig();
  271. $this->_decLevelConfig = Cache::getDecLevelConfig();
  272. $this->_empLevelConfig = Cache::getEmpLevelConfig();
  273. $this->_starCrownLevelConfig = Cache::getStarCrownLevelConfig();
  274. $this->_decRoleConfig = CalcCache::getDecRoleConfig($this->_periodNum);
  275. $this->_periodId = $periodDataArr['ID'];
  276. $this->_isPerpare = $periodDataArr['IS_PREPARE'];
  277. $this->_isCalcMonth = $periodObj->isCalcMonth($this->_periodNum);
  278. $this->_calcYear = $periodObj->getYear($this->_periodNum);
  279. $this->_calcMonth = $periodObj->getMonth($this->_periodNum);
  280. $this->_calcYearMonth = $periodObj->getYearMonth($this->_periodNum);
  281. }
  282. // 校验是否能开始进行计算
  283. public function checkStart() {
  284. if ($this->_isPerpare == 3) {
  285. return true;
  286. }
  287. return false;
  288. }
  289. /**
  290. * 设置结算状态
  291. * @param $type
  292. * start|end|fail
  293. */
  294. public function setCalcStatus($type) {
  295. if ($type == 'start') {
  296. Period::updateAll(['IS_CALCING' => 1, 'IS_CALCULATED' => Period::CALCULATE_NONE, 'CALCULATE_STARTED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  297. } elseif ($type == 'end') {
  298. Period::updateAll(['IS_CALCING' => 0, 'IS_CALCULATED' => Period::CALCULATE_FINISH, 'CALCULATED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  299. } elseif ($type == 'fail') {
  300. Period::updateAll(['IS_CALCING' => 0, 'IS_CALCULATED' => Period::CALCULATE_FAIL, 'CALCULATED_AT' => 0], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  301. }
  302. }
  303. /**
  304. * 清空相关表数据
  305. */
  306. public function clearCalcTableData() {
  307. // 奖金表
  308. CalcBonus::pageDeleteAll('1=1');
  309. CalcBonusQY::pageDeleteAll('PERIOD_NUM='.$this->_periodNum);
  310. CalcBonusBD::pageDeleteAll('1=1'); // 实际上是服务奖流水表
  311. CalcBonusTG::pageDeleteAll('1=1');
  312. // 月结时要清空的数据
  313. if ($this->_isCalcMonth) {
  314. CalcBonusTourism::pageDeleteAll('1=1');
  315. CalcBonusGarage::pageDeleteAll('1=1');
  316. CalcBonusVilla::pageDeleteAll('1=1');
  317. //计算服务不存储数据,存储过程表清空
  318. BsBonus103Calc::pageDeleteAll('1=1');
  319. BsBonus103Calc1::pageDeleteAll('1=1');
  320. BsBonus103CalcNet::pageDeleteAll('1=1');
  321. BsBonus103CalcNet1::pageDeleteAll('1=1');
  322. BsBonus103Test::pageDeleteAll('1=1');
  323. BsBonus103TestNet::pageDeleteAll('1=1');
  324. CalcBonusBsCalc::pageDeleteAll('1=1');
  325. CalcBonusBsChecking::pageDeleteAll('1=1');
  326. CalcBonusBsDetail::pageDeleteAll('1=1');
  327. CalcBonusBsDetailBonus::pageDeleteAll('1=1');
  328. CalcBonusBsDetailCalc::pageDeleteAll('1=1');
  329. CalcBonusBsDetailGpv::pageDeleteAll('1=1');
  330. CalcBonusBsStat::pageDeleteAll('1=1');
  331. QtrCalcUser::pageDeleteAll('1=1');
  332. QtrCalcScore::pageDeleteAll('1=1');
  333. QtrCalcRecord::pageDeleteAll('1=1');
  334. QtrCalcWeight::pageDeleteAll('1=1');
  335. CalcBonusBS::pageDeleteAll('1=1');
  336. CalcBonusTourism::pageDeleteAll('1=1');
  337. CalcBonusVilla::pageDeleteAll('1=1');
  338. }
  339. }
  340. // 修正期业绩表中,用户的最新聘级字段
  341. public function loopUpdatePeriod($offset = 0) {
  342. echo sprintf("时间:[%s]修正期业绩表最新聘级字段,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  343. $allData = PerfPeriod::findUseDbCalc()
  344. ->where('PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum])
  345. ->offset($offset)
  346. ->limit($this->_limit)
  347. ->asArray()
  348. ->all();
  349. if ($allData) {
  350. foreach ($allData as $data) {
  351. $nowBsEmpLv = $this->_nowLastEmpLv($data['USER_ID']);
  352. // 期结算结果
  353. PerfPeriod::updateAll(['LAST_EMP_LV' => $nowBsEmpLv], 'USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM',
  354. [
  355. ':USER_ID' => $data['USER_ID'],
  356. ':PERIOD_NUM' => $this->_periodNum
  357. ]
  358. );
  359. }
  360. unset($allData);
  361. return $this->loopUpdatePeriod($offset + $this->_limit);
  362. }
  363. unset($allData);
  364. return true;
  365. }
  366. // 修正月业绩表中,用户最新聘级
  367. public function loopUpdateMonthPerf($offset = 0) {
  368. echo sprintf("时间:[%s]修正月业绩表最新聘级字段,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  369. $allData = PerfMonth::findUseDbCalc()
  370. ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
  371. ->offset($offset)
  372. ->limit($this->_limit)
  373. ->asArray()
  374. ->all();
  375. if ($allData) {
  376. foreach ($allData as $data) {
  377. $nowBsEmpLv = $this->_nowLastEmpLv($data['USER_ID']);
  378. // 期结算结果
  379. PerfMonth::updateAll(['LAST_EMP_LV' => $nowBsEmpLv], 'USER_ID=:USER_ID AND CALC_MONTH=:CALC_MONTH',
  380. [
  381. ':USER_ID' => $data['USER_ID'],
  382. ':CALC_MONTH' => $this->_calcYearMonth
  383. ]
  384. );
  385. }
  386. unset($allData);
  387. return $this->loopUpdateMonthPerf($offset + $this->_limit);
  388. }
  389. unset($allData);
  390. return true;
  391. }
  392. /**
  393. * 推广奖
  394. * @param int $offset
  395. * @return bool
  396. * @throws \yii\db\Exception
  397. */
  398. public function calcBonusTG(int $offset = 0) {
  399. $periodNum = $this->_periodNum;
  400. // 从缓存获取分页有业绩的会员信息
  401. $allData = CalcCache::getHasPerfUsers($this->_periodNum, $offset, $this->_limit);
  402. if ($allData) {
  403. $insertBonusData = [];
  404. foreach ($allData as $userId) {
  405. // 从缓存中获取会员的业绩信息
  406. $perfData = CalcCache::nowPeriodPerf($userId, $periodNum);
  407. if( !$perfData ) continue;
  408. //个人业绩都算推荐奖,包括报单和复消、二次购物
  409. $perfPv = $perfData['PV_PCS_ZC'] ?? 0;
  410. if( $perfPv <= 0 ) continue;
  411. //推广奖使用个人PCS
  412. $recBonus = Tool::formatPrice($perfPv * $this->_sysConfig['recPercent']['VALUE'] / 100);
  413. if ($recBonus <= 0) continue;
  414. // 把对碰后的奖金存入缓存中
  415. $perfUserInfo = CalcCache::getUserInfo($userId, $periodNum);
  416. $bonusUserId = $perfUserInfo['REC_UID'] ?? '';
  417. if( !$bonusUserId ) continue;
  418. // 获取会员的报单级别
  419. $userBaseInfo = CalcCache::getUserInfo($bonusUserId, $this->_periodNum);
  420. CalcCache::bonus($bonusUserId, $this->_periodNum, 'BONUS_TG', $recBonus);
  421. //来源会员信息
  422. $fromUserInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  423. //推广奖流水
  424. $insertBonusData[] = [
  425. 'ID' => SnowFake::instance()->generateId(),
  426. 'USER_ID' => $bonusUserId,
  427. 'LAST_DEC_LV' => $userBaseInfo['DEC_LV'],
  428. 'LAST_EMP_LV' => $this->_nowLastEmpLv($userId),
  429. 'LAST_STATUS' => $userBaseInfo['STATUS'],
  430. 'FROM_USER_ID' => $userId,
  431. 'LAST_FROM_DEC_LV' => $fromUserInfo['DEC_LV'],
  432. 'LAST_FROM_EMP_LV' => $fromUserInfo['EMP_LV'],
  433. 'LAST_FROM_STATUS' => $fromUserInfo['STATUS'],
  434. 'AMOUNT' => $recBonus,
  435. 'ORI_BONUS' => $recBonus,
  436. 'PERIOD_NUM' => $this->_periodNum,
  437. 'CALC_YEAR' => $this->_calcYear,
  438. 'CALC_MONTH' => $this->_calcYearMonth,
  439. 'CREATED_AT' => Date::nowTime(),
  440. 'LOGS' => json_encode([
  441. 'perfPv' => $perfPv,
  442. 'recPercentConfig' => $this->_sysConfig['recPercent']['VALUE'],
  443. 'decLevel' => $userBaseInfo['DEC_LV'],
  444. ]),
  445. ];
  446. unset($perfData, $perfPv, $perfUserInfo, $recBonus, $bonusUserId, $userBaseInfo, $userId, $deductData, $fromUserInfo);
  447. }
  448. CalcBonusTG::batchInsert($insertBonusData);
  449. unset($allData, $insertBonusData);
  450. return $this->calcBonusTG($offset + $this->_limit);
  451. }
  452. unset($allData);
  453. return true;
  454. }
  455. /**
  456. * 服务奖第一步
  457. * @param int $offset
  458. * @return bool
  459. * @throws \yii\db\Exception
  460. */
  461. public function calcBonusBDStepOne(int $offset = 0) {
  462. echo sprintf("时间:[%s]服务奖第【1】步,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  463. $periodNum = $this->_periodNum;
  464. // 从缓存获取分页有业绩的会员信息
  465. $allData = CalcCache::getHasPerfUsers($this->_periodNum, $offset, $this->_limit);
  466. if ($allData) {
  467. $insertBonusData = [];
  468. foreach ($allData as $userId) {
  469. // 从缓存中获取会员的业绩信息
  470. $perfData = CalcCache::nowPeriodPerf($userId, $periodNum);
  471. if( !$perfData ) continue;
  472. $decRoleBonusFrom = explode(',', $this->_sysConfig['decRoleBonusFrom']['VALUE']);
  473. $validPvPcs = 0;
  474. foreach ($decRoleBonusFrom as $orderType) {
  475. $orderTypeName = sprintf('PV_PCS_%s', $orderType);
  476. $orderTypeValue = $perfData[$orderTypeName] ?? 0;
  477. $coefficientName = self::ORDER_TYPE_TO_FW_COEFFICIENT[$orderType];
  478. $coefficient = $this->_sysConfig[$coefficientName]['VALUE'] ?? 1;
  479. $validPvPcs += $orderTypeValue * $coefficient;
  480. unset($orderType, $orderTypeName, $orderTypeValue, $coefficientName, $coefficient);
  481. }
  482. unset($perfData, $decRoleBonusFrom);
  483. if ( $validPvPcs <= 0 ) continue;
  484. $this->loopRelationParentDo($userId, function ($parent) use($userId, $validPvPcs){
  485. try {
  486. //判断parent的报单中心级别 和 服务奖比例
  487. $bonusUserId = $parent['PARENT_UID'];
  488. //计算级别之后更新过userInfo的缓存,缓存中级别发生了变化
  489. $bonusUserInfo = CalcCache::getUserInfo($bonusUserId, $this->_periodNum);
  490. $isDec = $bonusUserInfo['IS_DEC'];
  491. if($isDec == 0) return self::LOOP_CONTINUE;
  492. $decRoleId = $bonusUserInfo['DEC_ROLE_ID'];
  493. if( !$decRoleId ) return self::LOOP_CONTINUE;
  494. if( !isset($this->_decRoleConfig[$decRoleId]) ) return self::LOOP_CONTINUE;
  495. $parentDecRoleLevel = $this->_decRoleConfig[$decRoleId];
  496. $parentFwBonusPercent = $parentDecRoleLevel['FW_BONUS_PERCENT'] ?? 0;
  497. $cacheMaxPercent = CalcCache::fwMaxBonusPercent($userId, $this->_periodNum);
  498. $diffPercent = $parentFwBonusPercent - $cacheMaxPercent;
  499. if( $diffPercent <= 0 ) return self::LOOP_CONTINUE;
  500. $fwBonus = $validPvPcs * $diffPercent / 100;
  501. if( $fwBonus <= 0 ) return self::LOOP_CONTINUE;
  502. //给本人添加服务奖比例
  503. CalcCache::fwMaxBonusPercent($userId, $this->_periodNum, $parentFwBonusPercent);
  504. //记录奖金和奖金来源到缓存 并实现在缓存中奖金累加
  505. CalcCache::saveFwBonusList($bonusUserId, $this->_periodNum, $fwBonus, ['fromUid'=>$userId, 'fromPvPcs'=>$validPvPcs]);
  506. CalcCache::addHasFwBonusUsers($bonusUserId, $this->_periodNum);
  507. } catch(Exception $e) {
  508. var_dump($e->getMessage(), '------------');
  509. }
  510. unset($bonusUserId, $bonusUserInfo, $isDec, $decRoleId, $parentDecRoleLevel, $parentFwBonusPercent, $cacheMaxPercent, $diffPercent, $fwBonus);
  511. });
  512. unset($userId, $validPvPcs);
  513. }
  514. unset($allData, $insertBonusData);
  515. return $this->calcBonusBDStepOne($offset + $this->_limit);
  516. }
  517. unset($allData);
  518. return true;
  519. }
  520. /**
  521. * 服务奖第二步
  522. * @param int $offset
  523. * @return bool
  524. * @throws \yii\db\Exception
  525. */
  526. public function calcBonusBDStepTwo(int $offset = 0) {
  527. echo sprintf("时间:[%s]服务奖第【2】步,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  528. $allData = CalcCache::getHasFwBonusUsers($this->_periodNum, $offset, $this->_limit);
  529. if ($allData) {
  530. $insertBonusData = [];
  531. foreach ($allData as $userId) {
  532. $fwBonusData = CalcCache::getFwBonusList($userId, $this->_periodNum);
  533. if( !$fwBonusData ) continue;
  534. $fwBonus = $fwBonusData['fwBonus'] ?? 0;
  535. if( $fwBonus <=0 ) continue;
  536. //总金额限制
  537. try {
  538. $userBaseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  539. CalcCache::bonus($userId, $this->_periodNum, 'BONUS_BD', $fwBonus);
  540. $decRoleId = $userBaseInfo['DEC_ROLE_ID'];
  541. $insertBonusData[] = [
  542. 'ID' => SnowFake::instance()->generateId(),
  543. 'USER_ID' => $userId,
  544. 'LAST_DEC_LV' => $userBaseInfo['DEC_LV'],
  545. 'LAST_EMP_LV' => $this->_nowLastEmpLv($userId),
  546. 'LAST_STATUS' => $userBaseInfo['STATUS'],
  547. 'FROM_USER_ID' => $userId,
  548. 'LAST_FROM_DEC_LV' => $userBaseInfo['DEC_LV'],
  549. 'LAST_FROM_EMP_LV' => $userBaseInfo['EMP_LV'],
  550. 'LAST_FROM_STATUS' => $userBaseInfo['STATUS'],
  551. 'AMOUNT' => $fwBonus,
  552. 'ORI_BONUS' => $fwBonus,
  553. 'RECONSUME_POINTS' => 0,
  554. 'MANAGE_TAX' => 0,
  555. 'PERIOD_NUM' => $this->_periodNum,
  556. 'CALC_YEAR' => $this->_calcYear,
  557. 'CALC_MONTH' => $this->_calcYearMonth,
  558. 'CREATED_AT' => Date::nowTime(),
  559. 'LOGS' => json_encode([
  560. 'decRoleId' => $decRoleId,
  561. ])
  562. ];
  563. } catch(Exception $e) {
  564. var_dump('---->>>', $e->getMessage());exit;
  565. }
  566. unset($userId, $fwBonusData, $userBaseInfo, $decRoleId, $fwBonus);
  567. }
  568. CalcBonusBD::batchInsert($insertBonusData);
  569. unset($insertBonusData, $allData);
  570. $this->calcBonusBDStepTwo($offset + $this->_limit);
  571. }
  572. unset($allData);
  573. return true;
  574. }
  575. /**
  576. * 团队奖
  577. * @param int $offset
  578. * @return bool
  579. * @throws \yii\db\Exception
  580. */
  581. public function calcBonusQY(int $offset = 0) {
  582. echo sprintf("时间:[%s]团队奖,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  583. $periodNum = $this->_periodNum;
  584. // 从缓存获取分页有业绩的会员信息
  585. $allData = CalcCache::getHasPerfUsers($this->_periodNum, $offset, $this->_limit);
  586. if ($allData) {
  587. $insertBonusData = [];
  588. foreach ($allData as $userId) {
  589. // 从缓存中获取会员的业绩信息
  590. $perfData = CalcCache::nowPeriodPerf($userId, $periodNum);
  591. // 从缓存中获取会员的上期结余业绩信息
  592. $pervSurplusPerf = CalcCache::surplusPerf($userId, $periodNum);
  593. // 本期 + 上期结余
  594. $perfArr = [
  595. 'SURPLUS_1L' => $perfData['PV_1L_TOUCH'] + $pervSurplusPerf['SURPLUS_1L'],
  596. 'SURPLUS_2L' => $perfData['PV_2L_TOUCH'] + $pervSurplusPerf['SURPLUS_2L'],
  597. 'SURPLUS_3L' => $perfData['PV_3L_TOUCH'] + $pervSurplusPerf['SURPLUS_3L'],
  598. 'SURPLUS_4L' => $perfData['PV_4L_TOUCH'] + $pervSurplusPerf['SURPLUS_4L'],
  599. 'SURPLUS_5L' => $perfData['PV_5L_TOUCH'] + $pervSurplusPerf['SURPLUS_5L'],
  600. ];
  601. $oriPerfArr = [
  602. 'perfArr' => $perfArr,
  603. 'touchBonus' => 0,
  604. ];
  605. // 获取会员的报单级别
  606. $userBaseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  607. $decLevelConfig = $this->_decLevelConfig;
  608. $nowDecLevelConfig = $decLevelConfig[$userBaseInfo['DEC_LV']];
  609. // 对碰
  610. $touchBonusArr = $this->touchPerf($oriPerfArr, $perfArr, $nowDecLevelConfig['QY_PERCENT']/100);
  611. $touchPerfArr = [];
  612. foreach ($touchBonusArr['perfArr'] as $keyR => $perfR) {
  613. $touchPerfArr[$keyR] = $perfR;
  614. }
  615. // 对碰完成后把结余的业绩存入本期业绩缓存中
  616. CalcCache::nowPeriodPerf($userId, $periodNum, $touchPerfArr);
  617. //更新数据库
  618. PerfPeriod::updateAll($touchPerfArr, 'USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM', [
  619. 'USER_ID' => $userId,
  620. 'PERIOD_NUM' => $periodNum,
  621. ]);
  622. if ($touchBonusArr['touchBonus'] <= 0) continue;
  623. $teamBonus = $touchBonusArr['touchBonus'];
  624. $capBonusQy = $teamBonus; // 封顶前的奖金
  625. //判断级别上限,个人奖金封顶限制
  626. $teamBonus = $this->declarationLevelCap($teamBonus, $userId, $userBaseInfo['DEC_LV']);
  627. if( $teamBonus <= 0 ) continue;
  628. // 将封顶前的金额加入用户奖金缓存中,此金额不能发放(总奖金,总实际奖金)
  629. CalcCache::bonus($userId, $periodNum, 'CAPPED_BONUS_QY', $capBonusQy);
  630. // TODO:取小腿值
  631. $payLeg = min([$perfArr['SURPLUS_1L'], $perfArr['SURPLUS_2L']]);
  632. // 计算荣衔星级
  633. $starCrown = StarCrownLevel::getStarCrown($payLeg);
  634. // 是否活跃
  635. $isActive = $this->_isPerfActive($userId);
  636. $oriBonus = $isActive ? $teamBonus : 0;
  637. $lastCrownLv = $isActive ? $starCrown['ID'] : StarCrownLevel::getDefaultLevelId();
  638. //团队奖流水
  639. $insertBonusData[] = [
  640. 'ID' => SnowFake::instance()->generateId(),
  641. 'USER_ID' => $userId,
  642. 'ORI_CAPPED_BONUS_QY' => $capBonusQy,
  643. 'LAST_DEC_LV' => $userBaseInfo['DEC_LV'],
  644. 'LAST_EMP_LV' => $this->_nowLastEmpLv($userId),
  645. 'LAST_CROWN_LV' => $lastCrownLv,
  646. 'LAST_STATUS' => $userBaseInfo['STATUS'],
  647. 'AMOUNT' => $oriBonus,
  648. 'ORI_BONUS' => $oriBonus,
  649. 'PERIOD_NUM' => $this->_periodNum,
  650. 'CALC_YEAR' => $this->_calcYear,
  651. 'CALC_MONTH' => $this->_calcYearMonth,
  652. 'CREATED_AT' => Date::nowTime(),
  653. 'LOGS' => json_encode([
  654. 'perfArr' => $perfArr,
  655. 'touchPerfArrOri' => $touchBonusArr['perfArr'],
  656. 'touchPerfArr' => $touchPerfArr,
  657. 'nowDecLevelConfig' => $nowDecLevelConfig,
  658. 'decLevel' => $userBaseInfo['DEC_LV'],
  659. ]),
  660. 'IS_ACTIVE' => (int)$isActive,
  661. 'HOPE_CROWN_LV' => $starCrown['ID'],
  662. 'HOPE_BONUS' => $teamBonus,
  663. ];
  664. // 星级放入缓存
  665. CalcCache::addUserStarCrown($userId, $periodNum, $lastCrownLv);
  666. if ($oriBonus > 0) {
  667. // 把对碰后的奖金存入缓存中
  668. // CalcCache::bonus($userId, $periodNum, 'BONUS_QY', $oriBonus, $deductData);
  669. CalcCache::bonus($userId, $periodNum, 'BONUS_QY', $teamBonus);
  670. }
  671. unset($perfData, $pervSurplusPerf, $perfArr, $oriPerfArr, $touchPerfArr, $userBaseInfo, $decLevelConfig, $touchBonusArr, $userId, $nowDecLevelConfig, $teamBonus, $deductData);
  672. }
  673. CalcBonusQY::batchInsert($insertBonusData);
  674. unset($allData, $insertBonusData);
  675. return $this->calcBonusQY($offset + $this->_limit);
  676. }
  677. unset($allData);
  678. return true;
  679. }
  680. /**
  681. * 季度奖计算
  682. */
  683. public function calcQuarter() {
  684. if( !$this->_isCalcMonth || !in_array($this->_calcMonth, [3,6,9,12])) {
  685. // echo('不是季结点,进这里,不计算季度奖'. PHP_EOL);
  686. return false;
  687. }
  688. $result = \Yii::$app->db->createCommand("CALL QtrCalc(:periodNum)")
  689. ->bindValue(':periodNum' , $this->_periodNum )
  690. ->execute();
  691. return $result;
  692. }
  693. // 执行蓝星管理奖金的存储过程
  694. public function calcBsProcedure() {
  695. if( !$this->_isCalcMonth ) {
  696. // 不是结算月,则不进行计算
  697. return false;
  698. }
  699. $result = \Yii::$app->db->createCommand("CALL CalcBlue(:periodNum)")
  700. ->bindValue(':periodNum' , $this->_periodNum )
  701. ->execute();
  702. return $result;
  703. }
  704. // 执行旅游奖的计算
  705. public function calcBonusTourism() {
  706. // 月结,如果不是月结点,则直接退出
  707. if (!$this->_isCalcMonth) {
  708. return true;
  709. }
  710. $bonusConfig = $this->_sysConfig['openTourism'];
  711. // 达标条件:聘级、级别、奖项比例
  712. $config = json_decode($bonusConfig['OPTIONS'], true);
  713. // 奖金总比例
  714. $mate = $bonusConfig['VALUE'] / 100;
  715. // 会员级别
  716. $minDecLevel = $config['OPTIONS']['declarationLevel'] ?? [];
  717. // 月度公司总PV
  718. $monthTotalPV = PerfMonth::find()
  719. ->yearMonth($this->_calcYearMonth)
  720. ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
  721. ->sum('PV_PCS');
  722. // 用于分发的奖金总数
  723. $transferAmount = $monthTotalPV * $mate;
  724. // 基于蓝星奖结果计算符合获奖条件的会员StarDirector
  725. $userStarDirector = CalcBonusBS::find()
  726. ->yearMonth($this->_calcYearMonth)
  727. ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
  728. ->select('USER_ID,LEVEL_ID,LAST_DEC_LV,LAST_EMP_LV,LAST_STATUS')
  729. ->groupBy('USER_ID')
  730. ->asArray()
  731. ->all();
  732. $userStarDirectorObj = array_column($userStarDirector, NULL, 'USER_ID');
  733. // 基于团队奖/绩效奖结果计算会员的StarCrown
  734. $userStarCrown = CalcBonusQY::find()
  735. ->yearMonth($this->_calcYearMonth)
  736. ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
  737. ->select('USER_ID,LAST_CROWN_LV')
  738. ->groupBy('USER_ID')
  739. ->asArray()
  740. ->all();
  741. $userStarCrownObj = array_column($userStarCrown, NULL, 'USER_ID');
  742. // 合并用户ID,去重
  743. $bonusUsers = array_unique(array_merge(array_keys($userStarDirectorObj), array_keys($userStarCrownObj)));
  744. // 奖金点数综合
  745. $bonusPointComplex = 0;
  746. $insertBonusData = [];
  747. foreach($bonusUsers as $userId) {
  748. // 计算奖金:取starDirectorPoint和starCrownPoint的大个值
  749. $starDirectorPoint = $this->_empLevelConfig[$userStarDirectorObj[$userId]['LEVEL_ID']]['TOURISM_PERCENT'] ?? 0;
  750. $starCrownPoint = $this->_starCrownLevelConfig[$userStarCrownObj[$userId]['LAST_CROWN_LV']]['TOURISM_PERCENT'] ?? 0;
  751. // 奖金比例:
  752. $bonusPoint = max($starDirectorPoint, $starCrownPoint);
  753. if ($bonusPoint <= 0) {
  754. continue;
  755. }
  756. $insertBonusData[] = [
  757. 'ID' => SnowFake::instance()->generateId(),
  758. 'USER_ID' => $userId,
  759. 'LAST_DEC_LV' => $userStarDirectorObj[$userId]['LAST_DEC_LV'],
  760. 'LAST_EMP_LV' => $userStarDirectorObj[$userId]['LEVEL_ID'],
  761. 'LAST_STATUS' => $userStarDirectorObj[$userId]['LAST_STATUS'],
  762. 'LAST_CROWN_LV' => $userStarCrownObj[$userId]['LAST_CROWN_LV'],
  763. 'AMOUNT_STANDARD' => 0,
  764. 'POINT' => $bonusPoint,
  765. 'PERIOD_NUM' => $this->_periodNum,
  766. 'CALC_YEAR' => $this->_calcYear,
  767. 'CALC_MONTH' => $this->_calcYearMonth,
  768. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  769. 'CREATED_AT' => Date::nowTime(),
  770. 'PERF' => $monthTotalPV,
  771. 'TRANSFER_RATE' => $mate,
  772. 'TRANSFER_AMOUNT' => Tool::formatPrice($transferAmount),
  773. 'CAP_AMOUNT' => 0,
  774. 'POINT_COMPLEX' => 0,
  775. ];
  776. $bonusPointComplex += $bonusPoint;
  777. }
  778. // 数据写入总表
  779. if ($insertBonusData) {
  780. foreach ($insertBonusData as &$bonusData) {
  781. // 计算奖金
  782. $amount = Tool::formatPrice($transferAmount * ($bonusData['POINT'] / $bonusPointComplex));
  783. if ($amount <= 0) {
  784. continue;
  785. }
  786. // 会员级别达到要求才会发放奖金
  787. if ($bonusData['LAST_DEC_LV'] == $minDecLevel) {
  788. // 放入缓存
  789. CalcCache::tourismBonus($bonusData['USER_ID'], $this->_periodNum, $amount);
  790. }
  791. $bonusData['AMOUNT'] = $amount;
  792. $bonusData['POINT_COMPLEX'] = $bonusPointComplex;
  793. }
  794. CalcBonusTourism::batchInsert($insertBonusData);
  795. }
  796. return true;
  797. }
  798. // 执行房奖的计算
  799. public function calcBonusVilla() {
  800. if (!$this->_isCalcMonth) {
  801. return true;
  802. }
  803. $bonusConfig = $this->_sysConfig['openVilla'];
  804. // 达标条件:聘级、级别、奖项比例
  805. $config = json_decode($bonusConfig['OPTIONS'], true);
  806. // 奖金总比例
  807. $mate = $bonusConfig['VALUE'] / 100;
  808. // 个人奖金封顶
  809. $capBonus = intval($this->_sysConfig['openVillaCap']['VALUE'] ?? 0);
  810. // 会员级别
  811. $minDecLevel = $config['declarationLevel'] ?? [];
  812. // 月度公司总PV
  813. $monthTotalPV = PerfMonth::find()
  814. ->yearMonth($this->_calcYearMonth)
  815. ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
  816. ->sum('PV_PCS');
  817. // 用于分发的奖金总数
  818. $transferAmount = $monthTotalPV * $mate;
  819. // 基于团队奖/绩效奖结果计算会员的StarCrown.StarCrown基于周期计算,一个月会产生多次,取月周期中的最高星级
  820. $subQuery = CalcBonusQY::find()
  821. ->yearMonth($this->_calcYearMonth)
  822. ->where('CALC_MONTH = :CALC_MONTH AND LAST_CROWN_LV <> :NO_CROWN_LV', [':CALC_MONTH' => $this->_calcYearMonth, ':NO_CROWN_LV' => StarCrownLevel::NO_LEVEL_ID])
  823. ->select('USER_ID,LAST_DEC_LV,LAST_CROWN_LV,LAST_STATUS,LEVEL_NAME,SORT')
  824. ->joinWith(['starCrown' => function($query) {
  825. $query->select(['LEVEL_NAME', 'SORT']);
  826. }])
  827. ->having(1)
  828. ->orderBy('USER_ID ASC, SORT DESC');
  829. $userStarCrownObj = (new Query())->from(['u' => $subQuery])->select('USER_ID,LAST_DEC_LV,LAST_CROWN_LV,LAST_STATUS,LEVEL_NAME,SORT')->groupBy('USER_ID')->indexBy('USER_ID')->all();
  830. // 奖金点数综合
  831. $bonusPointComplex = 0;
  832. $insertBonusData = [];
  833. foreach($userStarCrownObj as $item) {
  834. // 奖金比例
  835. $bonusPoint = $this->_starCrownLevelConfig[$item['LAST_CROWN_LV']]['VILLA_PERCENT'] ?? 0;
  836. if (!$bonusPoint) {
  837. continue;
  838. }
  839. // 会员级别达到要求才会发放奖金
  840. if ($item['LAST_DEC_LV'] != $minDecLevel) {
  841. continue;
  842. }
  843. $insertBonusData[] = [
  844. 'ID' => SnowFake::instance()->generateId(),
  845. 'USER_ID' => $item['USER_ID'],
  846. 'LAST_DEC_LV' => $item['LAST_DEC_LV'] ?? '',
  847. 'LAST_EMP_LV' => $this->_nowLastEmpLv($item['USER_ID']),
  848. 'LAST_STATUS' => $item['LAST_STATUS'] ?? 0,
  849. 'LAST_CROWN_LV' => $item['LAST_CROWN_LV'] ?? '',
  850. 'AMOUNT' => 0,
  851. 'POINT' => $bonusPoint,
  852. 'PERIOD_NUM' => $this->_periodNum,
  853. 'CALC_YEAR' => $this->_calcYear,
  854. 'CALC_MONTH' => $this->_calcYearMonth,
  855. 'CREATED_AT' => Date::nowTime(),
  856. 'PERF' => $monthTotalPV,
  857. 'TRANSFER_RATE' => $mate,
  858. 'TRANSFER_AMOUNT' => Tool::formatPrice($transferAmount),
  859. 'CAP_AMOUNT' => 0,
  860. 'POINT_COMPLEX' => 0,
  861. ];
  862. $bonusPointComplex += $bonusPoint;
  863. }
  864. // 数据写入总表
  865. if ($insertBonusData) {
  866. // 计算个人奖金
  867. foreach ($insertBonusData as &$bonusData) {
  868. // 计算奖金
  869. $amount = Tool::formatPrice($transferAmount * ($bonusData['POINT'] / $bonusPointComplex));
  870. if ($amount <= 0) {
  871. continue;
  872. }
  873. // 封顶前奖金数
  874. $capAmount = $amount;
  875. // 奖金数不能大于封顶值
  876. $amount = ($amount > $capBonus) ? $capBonus : $amount;
  877. $bonusData['AMOUNT'] = $amount;
  878. $bonusData['CAP_AMOUNT'] = $capAmount;
  879. $bonusData['POINT_COMPLEX'] = $bonusPointComplex;
  880. // 放入缓存
  881. CalcCache::villaBonus($bonusData['USER_ID'], $this->_periodNum, $amount);
  882. }
  883. CalcBonusVilla::batchInsert($insertBonusData);
  884. }
  885. return true;
  886. }
  887. // 执行车奖的计算
  888. public function calcBonusGarage() {
  889. // 月结,如果不是月结点,则直接退出
  890. if (!$this->_isCalcMonth) {
  891. return true;
  892. }
  893. $bonusConfig = $this->_sysConfig['openGarage'];
  894. // 达标条件:聘级、级别、奖项比例
  895. $config = json_decode($bonusConfig['OPTIONS'], true);
  896. // 奖金总比例
  897. $mate = $bonusConfig['VALUE'] / 100;
  898. // 会员级别
  899. $minDecLevel = $config['declarationLevel'] ?? [];
  900. // 个人奖金封顶
  901. $capBonus = intval($this->_sysConfig['openGarageCap']['VALUE'] ?? 0);
  902. // 月度公司总PV
  903. $monthTotalPV = PerfMonth::find()
  904. ->yearMonth($this->_calcYearMonth)
  905. ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
  906. ->sum('PV_PCS');
  907. // 用于分发的奖金总数
  908. $transferAmount = $monthTotalPV * $mate;
  909. // 基于蓝星奖结果计算符合获奖条件的会员StarDirector
  910. $userStarDirector = CalcBonusBS::find()
  911. ->yearMonth($this->_calcYearMonth)
  912. ->where('CALC_MONTH = :CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
  913. ->select('USER_ID,LEVEL_ID,LAST_DEC_LV,LAST_STATUS')
  914. ->groupBy('USER_ID')
  915. ->asArray()
  916. ->all();
  917. $userStarDirectorObj = array_column($userStarDirector, NULL, 'USER_ID');
  918. // 基于团队奖/绩效奖结果计算会员的StarCrown.StarCrown基于周期计算,一个月会产生多次,取月周期中的最高星级
  919. $subQuery = CalcBonusQY::find()
  920. ->yearMonth($this->_calcYearMonth)
  921. ->where('CALC_MONTH = :CALC_MONTH AND LAST_CROWN_LV <> :NO_CROWN_LV', [':CALC_MONTH' => $this->_calcYearMonth, ':NO_CROWN_LV' => StarCrownLevel::NO_LEVEL_ID])
  922. ->select('USER_ID,LAST_DEC_LV,LAST_CROWN_LV,LAST_STATUS,LEVEL_NAME,SORT')
  923. ->joinWith(['starCrown' => function($query) {
  924. $query->select(['LEVEL_NAME', 'SORT']);
  925. }])
  926. ->having(1)
  927. ->orderBy('USER_ID ASC, SORT DESC');
  928. $userStarCrownObj = (new Query())->from(['u' => $subQuery])->select('USER_ID,LAST_DEC_LV,LAST_CROWN_LV,LAST_STATUS,LEVEL_NAME,SORT')->groupBy('USER_ID')->indexBy('USER_ID')->all();
  929. // 合并用户ID,去重
  930. $bonusUsers = array_unique(array_merge(array_keys($userStarDirectorObj), array_keys($userStarCrownObj)));
  931. sort($bonusUsers);
  932. // 奖金点数综合
  933. $bonusPointComplex = 0;
  934. $insertBonusData = [];
  935. foreach($bonusUsers as $userId) {
  936. // 计算奖金:取starDirectorPoint和starCrownPoint的大个值
  937. $starDirectorPoint = !isset($userStarDirectorObj[$userId]['LEVEL_ID']) ? 0 : ($this->_empLevelConfig[$userStarDirectorObj[$userId]['LEVEL_ID']]['GARAGE_PERCENT'] ?? 0);
  938. $starCrownPoint = !isset($userStarCrownObj[$userId]['LAST_CROWN_LV']) ? 0: ($this->_starCrownLevelConfig[$userStarCrownObj[$userId]['LAST_CROWN_LV']]['GARAGE_PERCENT'] ?? 0);
  939. // 奖金比例:
  940. $bonusPoint = max($starDirectorPoint, $starCrownPoint);
  941. if ($bonusPoint <= 0) {
  942. continue;
  943. }
  944. // 会员级别达到要求才会发放奖金
  945. $lastDecLv = $userStarDirectorObj[$userId]['LAST_DEC_LV'] ?? ($userStarCrownObj[$userId]['LAST_DEC_LV'] ?? '');
  946. if ($lastDecLv != $minDecLevel) {
  947. continue;
  948. }
  949. $insertBonusData[] = [
  950. 'ID' => SnowFake::instance()->generateId(),
  951. 'USER_ID' => $userId,
  952. 'LAST_DEC_LV' => $userStarDirectorObj[$userId]['LAST_DEC_LV'] ?? ($userStarCrownObj[$userId]['LAST_DEC_LV'] ?? ''),
  953. 'LAST_EMP_LV' => $userStarDirectorObj[$userId]['LEVEL_ID'] ?? '',
  954. 'LAST_STATUS' => $userStarDirectorObj[$userId]['LAST_STATUS'] ?? ($userStarCrownObj[$userId]['LAST_STATUS'] ?? 1),
  955. 'LAST_CROWN_LV' => $userStarCrownObj[$userId]['LAST_CROWN_LV'] ?? '',
  956. 'AMOUNT' => 0,
  957. 'POINT' => $bonusPoint,
  958. 'PERIOD_NUM' => $this->_periodNum,
  959. 'CALC_YEAR' => $this->_calcYear,
  960. 'CALC_MONTH' => $this->_calcYearMonth,
  961. 'CREATED_AT' => Date::nowTime(),
  962. 'PERF' => $monthTotalPV,
  963. 'TRANSFER_RATE' => $mate,
  964. 'TRANSFER_AMOUNT' => Tool::formatPrice($transferAmount),
  965. 'CAP_AMOUNT' => 0,
  966. 'POINT_COMPLEX' => 0,
  967. ];
  968. $bonusPointComplex += $bonusPoint;
  969. }
  970. // 数据写入总表
  971. if ($insertBonusData) {
  972. foreach ($insertBonusData as &$bonusData) {
  973. // 计算奖金
  974. $amount = Tool::formatPrice($transferAmount * ($bonusData['POINT'] / $bonusPointComplex));
  975. if ($amount <= 0) {
  976. continue;
  977. }
  978. // 封顶前奖金数
  979. $capAmount = $amount;
  980. // 奖金数不能大于封顶值
  981. $amount = ($amount > $capBonus) ? $capBonus : $amount;
  982. $bonusData['AMOUNT'] = $amount;
  983. $bonusData['CAP_AMOUNT'] = $capAmount;
  984. $bonusData['POINT_COMPLEX'] = $bonusPointComplex;
  985. // 放入缓存
  986. CalcCache::garageBonus($bonusData['USER_ID'], $this->_periodNum, $amount);
  987. }
  988. CalcBonusGarage::batchInsert($insertBonusData);
  989. }
  990. return true;
  991. }
  992. /**
  993. * 季度奖写用户缓存
  994. */
  995. public function calcQuarterUser(int $offset = 0) {
  996. if( !$this->_isCalcMonth || !in_array($this->_calcMonth, [3,6,9,12])) {
  997. // 不是结算月,则不进行计算
  998. return false;
  999. }
  1000. try{
  1001. $allData = CalcBonusQuarter::finduseDbCalc()
  1002. ->where('PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum])
  1003. ->groupBy('USER_ID')
  1004. ->offset($offset)
  1005. ->limit($this->_limit)
  1006. ->asArray()
  1007. ->all();
  1008. if ($allData){
  1009. // 达标条件:会员级别:钻卡
  1010. $config = json_decode($this->_sysConfig['openQuarter']['OPTIONS'], true);
  1011. $minDecLevel = $config['declarationLevel'] ?? [];
  1012. foreach ($allData as $user) {
  1013. // 管理奖钻卡发放
  1014. if ($user['LAST_DEC_LV'] == $minDecLevel) {
  1015. CalcCache::bonus($user['USER_ID'], $this->_periodNum, 'BONUS_QUARTER', $user['ORI_BONUS']);
  1016. }
  1017. }
  1018. return $this->calcQuarterUser($offset + $this->_limit);
  1019. }
  1020. } catch(Exception $e) {
  1021. var_dump($e->getMessage(),'--------------------');
  1022. }
  1023. unset($allData);
  1024. return true;
  1025. }
  1026. /**
  1027. * 蓝星管理奖金未拆分
  1028. */
  1029. public function calcBonusBsGL(int $offset = 0) {
  1030. if( !$this->_isCalcMonth ) {
  1031. // 不是结算月,则不进行计算
  1032. return false;
  1033. }
  1034. // 从缓存获取分页有收入的会员信息
  1035. $allData = CalcBonusBS::findUseDbCalc()
  1036. ->where('PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum])
  1037. ->groupBy('USER_ID')
  1038. ->offset($offset)
  1039. ->limit($this->_limit)
  1040. ->asArray()
  1041. ->all();
  1042. if ($allData) {
  1043. foreach ($allData as $user) {
  1044. // 是否活跃会员
  1045. $isActive = $this->_isPerfActive($user['USER_ID']);
  1046. $oriBonus = $isActive ? $user['ORI_BONUS'] : 0;
  1047. $lastEmpLv = $isActive ? $user['LEVEL_ID'] : EmployLevel::getDefaultLevelId();
  1048. if ($oriBonus > 0) {
  1049. CalcCache::bonus($user['USER_ID'], $this->_periodNum, 'BONUS_BS', $user['ORI_BONUS']);
  1050. }
  1051. // 如果不活跃,则不发放奖金,积分,级别
  1052. // 更新蓝星奖金存储过程的实发金额数据
  1053. CalcBonusBS::updateAll(
  1054. [
  1055. 'IS_ACTIVE' => (int)$isActive,
  1056. 'HOPE_EMP_LV' => $user['LEVEL_ID'],
  1057. 'LEVEL_ID' => $lastEmpLv,
  1058. 'HOPE_BONUS' => $user['ORI_BONUS']
  1059. ],
  1060. 'USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM',
  1061. [':USER_ID' => $user['USER_ID'], ':PERIOD_NUM' => $this->_periodNum]);
  1062. // 修改用户缓存中记录的用户的最新级别
  1063. // $userInfo = CalcCache::getUserInfo($user['USER_ID'], $this->_periodNum);
  1064. // $userInfo['LAST_EMP_LV'] = $lastEmpLv;
  1065. // CalcCache::setUserInfo($user['USER_ID'], $this->_periodNum, $userInfo);
  1066. }
  1067. return $this->calcBonusBsGL($offset + $this->_limit);
  1068. }
  1069. unset($allData);
  1070. return true;
  1071. }
  1072. /**
  1073. * 对碰
  1074. */
  1075. public function touchPerf(array $oriPerfArr, array $perfArr, $percent, $loopTimes=1) {
  1076. $resultArr = $oriPerfArr;
  1077. foreach ($perfArr as $keyT => $perfT) {
  1078. if ($perfT <= 0) {
  1079. unset($perfArr[$keyT]);
  1080. }
  1081. }
  1082. if (count($perfArr) >= 2 && $loopTimes < 6) {
  1083. arsort($perfArr, SORT_NUMERIC);
  1084. $onePerf = null;
  1085. $oneKey = null;
  1086. // $touchSurplusAmount = 0;
  1087. $touchAmount = 0;
  1088. // 前两个进行对碰
  1089. foreach ($perfArr as $key => $perf) {
  1090. if ($onePerf === null) {
  1091. $oneKey = $key;
  1092. $onePerf = $perf;
  1093. } else {
  1094. $touchSurplusAmount = $perf - $onePerf;
  1095. if ($touchSurplusAmount > 0) {
  1096. unset($perfArr[$oneKey]);
  1097. $resultArr['perfArr'][$oneKey] = 0;
  1098. $perfArr[$key] = $touchSurplusAmount;
  1099. $touchAmount = $onePerf;
  1100. } elseif ($touchSurplusAmount < 0) {
  1101. unset($perfArr[$key]);
  1102. $resultArr['perfArr'][$key] = 0;
  1103. $perfArr[$oneKey] = abs($touchSurplusAmount);
  1104. $touchAmount = $perf;
  1105. } else {
  1106. unset($perfArr[$oneKey], $perfArr[$key]);
  1107. $resultArr['perfArr'][$oneKey] = 0;
  1108. $resultArr['perfArr'][$key] = 0;
  1109. $touchAmount = $perf;
  1110. }
  1111. break;
  1112. }
  1113. }
  1114. $touchBonus = Tool::formatPrice($touchAmount * $percent);
  1115. $resultArr['touchBonus'] += $touchBonus;
  1116. foreach ($perfArr as $keyR => $perfR) {
  1117. $resultArr['perfArr'][$keyR] = $perfR;
  1118. }
  1119. return $this->touchPerf($resultArr, $perfArr, $percent, $loopTimes+1);
  1120. }
  1121. return $resultArr;
  1122. }
  1123. /**
  1124. * 循环父级并执行回调函数
  1125. * @param $userId
  1126. * @param callable $callbackFunc
  1127. * @param int $offset
  1128. * @return bool
  1129. */
  1130. public function loopNetworkParentDo($userId, callable $callbackFunc, int $offset = 0) {
  1131. $allParents = Cache::getAllNetworkParents($userId);
  1132. $allData = array_slice($allParents, $offset, $this->_limit);
  1133. unset($allParents);
  1134. if ($allData) {
  1135. foreach ($allData as $data) {
  1136. $funcResult = $callbackFunc($data);
  1137. if ($funcResult === self::LOOP_FINISH) {
  1138. return true;
  1139. } elseif ($funcResult === self::LOOP_CONTINUE) {
  1140. continue;
  1141. }
  1142. unset($data, $funcResult);
  1143. }
  1144. unset($allData);
  1145. return $this->loopNetworkParentDo($userId, $callbackFunc, $offset + $this->_limit);
  1146. }
  1147. return true;
  1148. }
  1149. /**
  1150. * 循环推荐网络的父级
  1151. * @param $userId
  1152. * @param callable $callbackFunc
  1153. * @param int $offset
  1154. * @return bool
  1155. */
  1156. public function loopRelationParentDo($userId, callable $callbackFunc, int $offset = 0) {
  1157. $allParents = Cache::getAllRelationParents($userId);
  1158. $allData = array_slice($allParents, $offset, $this->_limit);
  1159. unset($allParents);
  1160. if ($allData) {
  1161. foreach ($allData as $data) {
  1162. $funcResult = $callbackFunc($data);
  1163. if ($funcResult === self::LOOP_FINISH) {
  1164. return true;
  1165. } elseif ($funcResult === self::LOOP_CONTINUE) {
  1166. continue;
  1167. }
  1168. unset($data, $funcResult);
  1169. }
  1170. unset($allData);
  1171. return $this->loopRelationParentDo($userId, $callbackFunc, $offset + $this->_limit);
  1172. }
  1173. return true;
  1174. }
  1175. /**
  1176. * 按级别的收入上限
  1177. * 新的需求调整: 改成不按月进行限制,并且去掉vip奖
  1178. * @param $bonus
  1179. * @param $userId
  1180. * @param $declarationLevel
  1181. * @return float
  1182. */
  1183. public function declarationLevelCap($bonus, $userId, $declarationLevel) {
  1184. $decLevelConfig = $this->_decLevelConfig;
  1185. $nowDecLevelConfig = $decLevelConfig[$declarationLevel];
  1186. unset($decLevelConfig);
  1187. $maxGetBonus = $nowDecLevelConfig['INCOME_CAP'];
  1188. if( $bonus <= $maxGetBonus) {
  1189. return $bonus;
  1190. }else {
  1191. return $maxGetBonus;
  1192. }
  1193. }
  1194. /**
  1195. * 扣除管理费 返回管理费
  1196. */
  1197. public function deductManageTax($bonus) {
  1198. return $bonus * $this->_sysConfig['manageTaxPercent']['VALUE'] / 100;
  1199. }
  1200. // 扣除复消积分 返回应扣除的复消费积分
  1201. public function deductReconsumePonits($userId, $bonus) {
  1202. //判断是否达到了本月扣除复消的上限
  1203. $cacheData = CalcCache::monthLastPeriodReconsumePoints($userId, $this->_periodNum, $this->_calcYearMonth);
  1204. $bonusCache = CalcCache::bonus($userId, $this->_periodNum);
  1205. $lastPeriodPoints = '0.00';
  1206. if (!empty($cacheData)) {
  1207. $lastPeriodPoints = $cacheData['RECONSUME_POINTS_SUM'];
  1208. }
  1209. $reConsumePointsTotal = $bonusCache['RECONSUME_POINTS'] + $lastPeriodPoints;
  1210. $reConsumePointsCap = $this->_sysConfig['reConsumePointsMonthCap']['VALUE'];
  1211. unset($cacheData, $bonusCache, $lastPeriodPoints);
  1212. $reConsumePoints = 0;
  1213. if( $reConsumePointsTotal < $reConsumePointsCap ) {
  1214. $reConsumePoints = $bonus * $this->_sysConfig['reConsumePointsPercent']['VALUE'] / 100;
  1215. $reConsumePoints = min($reConsumePoints, $reConsumePointsCap-$reConsumePointsTotal);
  1216. }
  1217. unset($reConsumePointsTotal, $reConsumePointsCap);
  1218. return $reConsumePoints;
  1219. }
  1220. /**
  1221. * 更新百分比并发送
  1222. * @param $percent
  1223. */
  1224. private function _updatePercent($percent) {
  1225. // 把数据写入数据库中
  1226. Period::updateAll(['CALC_PERCENT' => $percent], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  1227. \Yii::$app->swooleAsyncTimer->pushAsyncPercentToAdmin($percent, ['MODEL' => 'PERIOD', 'ID' => $this->_periodId, 'FIELD' => 'CALC_PERCENT']);
  1228. }
  1229. /**
  1230. * 循环奖服务奖金会员,并入库
  1231. */
  1232. public function loopBonusUsers($offset = 0) {
  1233. echo sprintf("时间:[%s]缓存奖金数据入库,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  1234. // 从缓存列表里面从底层往上倒序获取会员
  1235. $allData = CalcCache::getHasBonusUsers($this->_periodNum, $offset, $this->_limit);
  1236. if($allData){
  1237. $insertDataBonus = [];
  1238. foreach($allData as $userId){
  1239. try {
  1240. $tempBonusData = $this->bonusData($userId);
  1241. if(!empty($tempBonusData['result'])){
  1242. $insertDataBonus[] = $tempBonusData['result'];
  1243. }
  1244. }catch(Exception $e) {
  1245. var_dump($e->getMessage(),'ssssssssssssssss',$userId);
  1246. }
  1247. unset($userId, $tempBonusData);
  1248. }
  1249. CalcBonus::batchInsert($insertDataBonus);
  1250. unset($insertDataBonus, $allData);
  1251. return $this->loopBonusUsers($offset + $this->_limit);
  1252. }
  1253. return true;
  1254. }
  1255. /**
  1256. * 奖金入库
  1257. */
  1258. public function bonusData($userId) {
  1259. // 车奖和房奖是积分,不参加到奖金累计中.季度奖季度发放,无最低消费限制
  1260. try{
  1261. $bonus = CalcCache::bonus($userId, $this->_periodNum);
  1262. $baseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  1263. $perfData = CalcCache::nowPeriodPerf($userId, $this->_periodNum);
  1264. $tourismBonus = CalcCache::tourismBonus($userId, $this->_periodNum);
  1265. $garageBonus = CalcCache::garageBonus($userId, $this->_periodNum);
  1266. $villaBonus = CalcCache::villaBonus($userId, $this->_periodNum);
  1267. $pervSurplusPerf = CalcCache::surplusPerf($userId, $this->_periodNum);
  1268. $starCrownLv = CalcCache::getUserStarCrown($userId, $this->_periodNum);// 星级
  1269. $bonusReal = '0.000'; // 总实发
  1270. $deductManageTaxItems = Config::DEDUCT_MANAGE_TAX; // 扣除管理费的列表
  1271. $deductReconsumePointsItems = Config::DEDUCT_RECONSUME_POINTS;// 扣除复消积分的列表
  1272. $totalReconsumePointSum = $totalManageSum = '0.000'; // 需要扣除的奖金项,扣除完之后的实发金额
  1273. $deductManageTax = '0.000'; // 扣除的管理费
  1274. $deductReconsumePoints = '0.000'; // 扣除的复消积分
  1275. foreach($deductManageTaxItems as $v) {
  1276. $totalManageSum+= $bonus[$v]; // 需要扣除管理费的累计之和
  1277. }
  1278. foreach($deductReconsumePointsItems as $v) {
  1279. $totalReconsumePointSum+= $bonus[$v]; // 需要扣除复消积分的累计之和
  1280. }
  1281. // 扣除管理费
  1282. if ($totalManageSum > 0) {
  1283. $deductManageTax = $this->deductManageTax($totalManageSum);
  1284. }
  1285. // 扣除复消积分
  1286. if ($totalReconsumePointSum > 0) {
  1287. $deductReconsumePoints = $this->deductReconsumePonits($userId, $totalReconsumePointSum);
  1288. }
  1289. // 总实发=总原奖金-扣除的总管理费-总复消积分
  1290. $bonusReal = $bonus['BONUS_TOTAL'] - $deductManageTax - $deductReconsumePoints;
  1291. // NG系统,不发放兑换积分
  1292. // 因为是最后统一发奖金,每个奖金得实发是和原奖金一样得.只不过要扣除管理费和复消积分.
  1293. // 当然,ng现在不扣除管理费和复消积分.所以都是0
  1294. $result = [
  1295. 'USER_ID' => $userId,
  1296. 'LAST_USER_NAME' => $baseInfo['USER_NAME'],
  1297. 'LAST_REAL_NAME' => $baseInfo['REAL_NAME'],
  1298. 'LAST_DEC_LV' => $baseInfo['DEC_LV'],
  1299. 'LAST_EMP_LV' => $this->_nowLastEmpLv($userId),
  1300. 'LAST_CROWN_LV' => $starCrownLv ?? StarCrownLevel::getDefaultLevelId(),
  1301. 'RECONSUME_POINTS' => $deductReconsumePoints,
  1302. 'MANAGE_TAX' => $deductManageTax, // 管理费
  1303. 'BONUS_REAL'=> $bonusReal,
  1304. 'BONUS_TOTAL'=>$bonus['BONUS_TOTAL'],
  1305. 'ORI_BONUS_BD' => $bonus['ORI_BONUS_BD'],
  1306. 'ORI_BONUS_TG' => $bonus['ORI_BONUS_TG'],
  1307. 'ORI_BONUS_BS' => $bonus['ORI_BONUS_BS'], // 蓝星管理奖金原奖金
  1308. 'ORI_BONUS_QY' => $bonus['ORI_BONUS_QY'],
  1309. 'ORI_CAPPED_BONUS_QY' => $bonus['ORI_CAPPED_BONUS_QY'], // 团队奖封顶前的奖金
  1310. 'ORI_BONUS_QUARTER' => $bonus['ORI_BONUS_QUARTER'],
  1311. 'ORI_BONUS_TOURISM' => $tourismBonus, // 旅游奖
  1312. 'ORI_BONUS_VILLA' => $villaBonus, // 房奖
  1313. 'ORI_BONUS_GARAGE' => $garageBonus, // 车奖
  1314. 'PV_1L' => $perfData['PV_1L_TOUCH'],//TOUCH为碰业绩
  1315. 'QY_1L' => $perfData['PV_1L_TOUCH'] + $pervSurplusPerf['SURPLUS_1L'],
  1316. 'SURPLUS_1L' => $perfData['SURPLUS_1L'],
  1317. 'PV_2L' => $perfData['PV_2L_TOUCH'],
  1318. 'QY_2L' => $perfData['PV_2L_TOUCH'] + $pervSurplusPerf['SURPLUS_2L'],
  1319. 'SURPLUS_2L' => $perfData['SURPLUS_2L'],
  1320. 'PV_3L' => $perfData['PV_3L_TOUCH'],
  1321. 'QY_3L' => $perfData['PV_3L_TOUCH'] + $pervSurplusPerf['SURPLUS_3L'],
  1322. 'SURPLUS_3L' => $perfData['SURPLUS_3L'],
  1323. 'PV_4L' => $perfData['PV_4L_TOUCH'],
  1324. 'QY_4L' => $perfData['PV_4L_TOUCH'] + $pervSurplusPerf['SURPLUS_4L'],
  1325. 'SURPLUS_4L' => $perfData['SURPLUS_4L'],
  1326. 'PV_5L' => $perfData['PV_5L_TOUCH'],
  1327. 'QY_5L' => $perfData['PV_5L_TOUCH'] + $pervSurplusPerf['SURPLUS_5L'],
  1328. 'SURPLUS_5L' => $perfData['SURPLUS_5L'],
  1329. 'PV_PCS' => $perfData['PV_PCS'],
  1330. 'PV_TOUCH' => Tool::formatPrice($perfData['PV_1L_TOUCH'] + $perfData['PV_2L_TOUCH'] + $perfData['PV_3L_TOUCH'] + $perfData['PV_4L_TOUCH'] + $perfData['PV_5L_TOUCH']),
  1331. 'PERIOD_NUM' => $this->_periodNum,
  1332. 'CALC_YEAR' => $this->_calcYear,
  1333. 'CALC_MONTH' => $this->_calcYearMonth,
  1334. 'CREATED_AT' => Date::nowTime(),
  1335. ];
  1336. } catch(Exception $e) {
  1337. var_dump($e->getMessage(),'-----------------------------------------------',$userId);
  1338. }
  1339. $resend = [];
  1340. unset($bonus, $bonusReal);
  1341. return ['result'=>$result,'resend'=>$resend];
  1342. }
  1343. // 判断是否满足月最低消费
  1344. public function _isMonthPerfLimit($userId) {
  1345. $userMonthTotal = PerfMonth::find()->where(
  1346. 'USER_ID=:USER_ID AND CALC_MONTH=:CALC_MONTH',
  1347. ['USER_ID'=>$userId, 'CALC_MONTH'=>$this->_calcYearMonth]
  1348. )
  1349. ->asArray()
  1350. ->one();
  1351. $fxPvStatus = false;
  1352. if (isset($userMonthTotal['PV_PCS']) && $userMonthTotal['PV_PCS'] >= $this->_sysConfig['monthPcsPvFxCondition']['VALUE']) {
  1353. $fxPvStatus = true;
  1354. }
  1355. return $fxPvStatus;
  1356. }
  1357. // 判断会员是否活跃
  1358. public function _isPerfActive($userId): bool
  1359. {
  1360. $pv = PerfPeriod::find()->where('USER_ID=:USER_ID AND CALC_MONTH=:CALC_MONTH AND PERIOD_NUM<=:PERIOD_NUM',
  1361. ['USER_ID'=>$userId, 'CALC_MONTH'=>$this->_calcYearMonth, 'PERIOD_NUM'=>$this->_periodNum])
  1362. ->SUM('PV_PCS');
  1363. return $pv >= $this->_sysConfig['monthPcsPvFxCondition']['VALUE'];
  1364. }
  1365. // 获取用户当期最新聘级
  1366. // 非结算月,直接获取传过来的用户级别.如果是结算月,bs流水表无数据则是无级别.有数据,则直接取缓存中数据.因为第一步计算bs奖已更新缓存数据
  1367. public function _nowLastEmpLv($userId) {
  1368. // 如果是结算月,且蓝星奖流水没记录,则为无级别
  1369. $lastEmpLv = EmployLevel::getDefaultLevelId();
  1370. $userBaseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  1371. if( $this->_isCalcMonth ) {
  1372. $hasBs = CalcBonusBS::findUseDbCalc()
  1373. ->where('PERIOD_NUM=:PERIOD_NUM AND USER_ID=:USER_ID', [':PERIOD_NUM' => $this->_periodNum, ':USER_ID' => $userId])
  1374. ->asArray()
  1375. ->one();
  1376. if (!empty($hasBs)) {
  1377. $lastEmpLv = $hasBs['LEVEL_ID'];
  1378. }
  1379. } else {
  1380. $lastEmpLv = $userBaseInfo['LAST_EMP_LV'];
  1381. }
  1382. return $lastEmpLv;
  1383. }
  1384. }