PerfCalc.php 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: liyunlong
  5. * Date: 2019-01-11
  6. * Time: 15:27
  7. */
  8. namespace common\helpers\bonus;
  9. use common\helpers\Cache;
  10. use common\helpers\Date;
  11. use common\helpers\Form;
  12. use common\helpers\snowflake\SnowFake;
  13. use common\helpers\Tool;
  14. use common\helpers\user\Info;
  15. use common\helpers\user\Reconsume;
  16. use common\models\CalcBonus;
  17. use common\models\DeclarationLevel;
  18. use common\models\forms\DeclarationForm;
  19. use common\models\Order;
  20. use common\models\OrderDec;
  21. use common\models\OrderShop;
  22. use common\models\OrderStandard;
  23. use common\models\PerfCompany;
  24. use common\models\PerfMonth;
  25. use common\models\PerfOrder;
  26. use common\models\PerfPeriod;
  27. use common\models\PerfStandard;
  28. use common\models\Period;
  29. use common\models\DecOrder;
  30. use common\models\EmployLevel;
  31. use common\models\UserRelation;
  32. use yii\base\Exception;
  33. use yii\base\StaticInstanceTrait;
  34. class PerfCalc {
  35. use StaticInstanceTrait;
  36. private $_limit = 1000;
  37. private $_handleUserId;
  38. private $_companyMonthPerf = 0;
  39. private $_cfTotalPercent = 0;
  40. private $_lxTotalPercent = 0;
  41. private $_sysConfig = [];
  42. private $_decLevelConfig = [];
  43. private $_empLevelConfig = [];
  44. private $_decRoleConfig = [];
  45. private $_errors = [];
  46. private $_periodNum = 0;
  47. private $_periodId;
  48. private $_isCalcMonth = 0;
  49. private $_calcYear;
  50. private $_calcMonth;
  51. private $_calcYearMonth;
  52. private $_lastCalcYear;
  53. private $_lastCalcMonth;
  54. private $_lastCalcYearMonth;
  55. private $_lastPeriodNum;
  56. private $_lastPeriodYear;
  57. private $_lastPeriodMonth;
  58. private $_lastPeriodYearMonth;
  59. //pv
  60. private $_pvRatio;
  61. const LOOP_FINISH = 1;
  62. const LOOP_CONTINUE = 2;
  63. const ORDER_PAY_TYPE_CASH = 'cash';
  64. // const ORDER_PAY_TYPE_POINT = 'point';
  65. /**
  66. * 设置期数
  67. * @param int $periodNum
  68. * @return int
  69. */
  70. public function setPeriodNum(int $periodNum) {
  71. return $this->_periodNum = $periodNum;
  72. }
  73. /**
  74. * 获取期数
  75. * @return int
  76. */
  77. public function getPeriodNum() {
  78. return $this->_periodNum;
  79. }
  80. /**
  81. * 加入错误错误
  82. * @param string $attr
  83. * @param string $error
  84. */
  85. public function addError(string $attr, string $error) {
  86. $this->_errors[$attr][] = $error;
  87. }
  88. /**
  89. * 获取错误信息
  90. * @return array
  91. */
  92. public function getErrors() {
  93. return $this->_errors;
  94. }
  95. /**
  96. * 计算步骤
  97. * @param $periodNum
  98. * @param null $handleUserId
  99. * @return bool
  100. */
  101. public function calcStep($periodNum, $handleUserId = null) {
  102. try {
  103. $this->_errors = [];
  104. $this->setPeriodNum($periodNum);
  105. $this->_handleUserId = $handleUserId;
  106. $t1 = microtime(true);
  107. // 初始化结算任务
  108. $this->initCalcTask();
  109. // 设置结算状态
  110. $this->setCalcStatus('start');
  111. // 清空所有本期结算用到的缓存
  112. CalcCache::clearAll($this->_periodNum);
  113. // 清空会员推荐和接点关系缓存
  114. CalcCache::clearNetCache();
  115. // 清空相关表数据
  116. $this->clearTableData();
  117. $t2 = microtime(true);
  118. echo('初始化、清空缓存及相关数据表完成,耗时:' . round($t2 - $t1, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  119. $this->_updatePercent(10);
  120. // 计算月奖,才需要向缓存中加入按推荐深度的所有用户
  121. //修改每一期都缓存所有用户
  122. CalcCache::addUsers($this->_periodNum);
  123. $t3 = microtime(true);
  124. echo('向缓存中加入用户完成,耗时:' . round($t3 - $t2, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  125. $this->_updatePercent(20);
  126. // 周结,循环向上级计入业绩并加入业绩单
  127. $this->loopCalcPeriodPerfByDecOrder();
  128. $this->loopCalcPeriodPerfByOrderDec();
  129. $t4 = microtime(true);
  130. echo('计算周业绩表中的数据完成,耗时:' . round($t4 - $t1, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  131. $this->_updatePercent(40);
  132. // 从会员的复销订单会员计算复销业绩并加入业绩单
  133. $this->loopCalcPerfByFXOrder();
  134. $this->loopCalcPerfByShopFXOrder();
  135. $t5 = microtime(true);
  136. echo('计算复销业绩并写入业绩单完成,耗时:' . round($t5 - $t4, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  137. $this->_updatePercent(60);
  138. //本期业绩入库
  139. $this->loopWriteNowPerf();
  140. $t6 = microtime(true);
  141. echo('本期业绩入库完成,耗时:' . round($t6 - $t5, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  142. $this->_updatePercent(70);
  143. //计算月业绩表中的数据
  144. $this->loopCalcMonthPerfTableData();
  145. $t7 = microtime(true);
  146. echo('计算月业绩表中的数据完成,耗时:' . round($t7 - $t6, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  147. $this->_updatePercent(80);
  148. //每月计算聘级
  149. // modify 聘级字段改成蓝星奖的 奖衔级别(蓝星奖的等级).不在使用荣衔奖金.聘级的等级切换成蓝星奖等级,
  150. // 此等级会影响团队将的封顶.
  151. //$this->loopCalcEmpLevel();
  152. $t8 = microtime(true);
  153. echo('计算聘级完成,耗时:' . round($t8 - $t7, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  154. //$this->_updatePercent(90);
  155. //本月业绩入库
  156. $this->loopWriteMonthPerf();
  157. $t7 = microtime(true);
  158. echo('本月业绩入库完成,耗时:' . round($t7 - $t6, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  159. $this->_updatePercent(95);
  160. // //达标业绩
  161. // $this->loopCalcPerfByStandardFXOrder();
  162. // //达标业绩入库
  163. // $this->loopWriteStandardPerf();
  164. $t8 = microtime(true);
  165. //echo('本月业绩入库完成,耗时:' . round($t8 - $t7, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  166. $this->_updatePercent(100);
  167. echo('结算全部完成,共耗时:' . round($t8 - $t1, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  168. } catch (\Exception $e) {
  169. $this->errorCalcTask();
  170. $this->addError('calc', $e->getMessage());
  171. return false;
  172. }
  173. return true;
  174. }
  175. /**
  176. * 结算完成
  177. * @return bool
  178. */
  179. public function endCalcTask() {
  180. // 更新结算状态
  181. $this->setCalcStatus('end');
  182. //如果自动结算
  183. if(boolval($this->_sysConfig['autoCalcPeriod']['VALUE'])){
  184. $period = Period::instance();
  185. if($period->isLastSent($this->_periodNum)) {
  186. $bonusCalc = BonusCalc::instance();
  187. $asyncResult = $bonusCalc->calcStep($this->_periodNum);
  188. if ($asyncResult) {
  189. $bonusCalc->endCalcTask();
  190. } else {
  191. $bonusCalc->errorCalcTask();
  192. }
  193. return $asyncResult;
  194. }
  195. }
  196. }
  197. /**
  198. * 结算错误
  199. */
  200. public function errorCalcTask() {
  201. // 清空所有本期结算用到的缓存
  202. CalcCache::clearAll($this->_periodNum);
  203. // 更新结算状态
  204. $this->setCalcStatus('fail');
  205. }
  206. /**
  207. * 设置生成业绩单状态
  208. * @param $type
  209. * start|end|fail
  210. */
  211. public function setCalcStatus($type) {
  212. if ($type == 'start') {
  213. Period::updateAll(['IS_PERFING' => 1, 'IS_PERFED' => Period::PERF_NONE, 'PERF_STARTED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  214. } elseif ($type == 'end') {
  215. Period::updateAll(['IS_PERFING' => 0, 'IS_PERFED' => Period::PERF_FINISH, 'PERFED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  216. } elseif ($type == 'fail') {
  217. Period::updateAll(['IS_PERFING' => 0, 'IS_PERFED' => Period::PERF_FAIL, 'PERFED_AT' => 0], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  218. }
  219. }
  220. /**
  221. * 初始化结算任务
  222. * @throws \yii\db\Exception
  223. */
  224. public function initCalcTask() {
  225. $this->_sysConfig = Cache::getSystemConfig();
  226. $this->_decLevelConfig = Cache::getDecLevelConfig();
  227. $this->_empLevelConfig = Cache::getEmpLevelConfig();
  228. $this->_decRoleConfig = CalcCache::getDecRoleConfig($this->_periodNum);
  229. $periodNum = $this->_periodNum;
  230. // 获取本年月和上年月
  231. $periodObj = Period::instance();
  232. $periodDataArr = $periodObj->setPeriodNum($periodNum);
  233. $this->_periodId = $periodDataArr['ID'];
  234. $this->_isCalcMonth = $periodObj->isCalcMonth($periodNum);
  235. $this->_calcYear = $periodObj->getYear($periodNum);
  236. $this->_calcMonth = $periodObj->getMonth($periodNum);
  237. $this->_calcYearMonth = $periodObj->getYearMonth($periodNum);
  238. $lastYearMonthArr = $periodObj->getLastMonth($periodNum);
  239. $this->_lastCalcYear = $lastYearMonthArr['year'];
  240. $this->_lastCalcMonth = $lastYearMonthArr['month'];
  241. $this->_lastCalcYearMonth = $lastYearMonthArr['yearMonth'];
  242. $this->_lastPeriodNum = $periodNum - 1;
  243. if (Period::isExistsPeriodNum($this->_lastPeriodNum)) {
  244. $this->_lastPeriodYear = $periodObj->getYear($this->_lastPeriodNum);
  245. $this->_lastPeriodMonth = $periodObj->getMonth($this->_lastPeriodNum);
  246. $this->_lastPeriodYearMonth = $periodObj->getYearMonth($this->_lastPeriodNum);
  247. } else {
  248. $this->_lastPeriodYear = 0;
  249. $this->_lastPeriodMonth = 0;
  250. $this->_lastPeriodYearMonth = 0;
  251. }
  252. $this->_pvRatio = $this->_sysConfig['pvRatio']['VALUE'];
  253. }
  254. /**
  255. * 清空相关表数据
  256. */
  257. public function clearTableData() {
  258. // 周业绩
  259. PerfPeriod::pageDeleteAll('PERIOD_NUM='.$this->_periodNum);
  260. // 业绩单
  261. PerfOrder::pageDeleteAll('PERIOD_NUM='.$this->_periodNum);
  262. // 月结时要清空的数据
  263. if ($this->_isCalcMonth) {
  264. // 月业绩表
  265. PerfMonth::pageDeleteAll("CALC_MONTH='{$this->_calcYearMonth}'");
  266. //达标业绩表
  267. PerfStandard::pageDeleteAll("CALC_MONTH='{$this->_calcYearMonth}'");
  268. }
  269. }
  270. /**
  271. * 周结,向上级算业绩,并计入业绩单
  272. * @param int $offset
  273. * @return bool
  274. * @throws \yii\db\Exception
  275. */
  276. public function loopCalcPeriodPerfByDecOrder($offset = 0) {
  277. // 循环获取全部报单
  278. $allData = DecOrder::findUseDbCalc()->select('ID,DEC_SN,ORDER_SN,USER_ID,TYPE,TO_USER_ID,IS_ADMIN,DEC_AMOUNT,DEC_PV,PERIOD_NUM,CALC_MONTH,IS_DEL,P_CALC_MONTH,CREATED_AT,DEC_ID')->where("PERIOD_NUM=:PERIOD_NUM AND IS_DEL=0 AND TYPE='ZC'", [':PERIOD_NUM' => $this->_periodNum])->orderBy('CREATED_AT DESC,ID DESC')->offset($offset)->limit($this->_limit)->asArray()->all();
  279. if ($allData) {
  280. $insertPerfOrderData = [];
  281. foreach ($allData as $data) {
  282. // 是否关停等状态不能拿业绩
  283. if (!$this->isHasPerf($data['TO_USER_ID'])) {
  284. continue;
  285. }
  286. //零售单不累计业绩,仅存业绩单
  287. // if($data['TYPE']!='LS') {
  288. // 给自己增加PCS(个人消费)
  289. CalcCache::nowPeriodPerf($data['TO_USER_ID'], $this->_periodNum, [
  290. 'PV_PCS' => $data['DEC_PV'],
  291. 'PV_PCS_ZC' => $data['DEC_PV'],
  292. ]);
  293. // 把该会员加入到能拿到业绩的会员缓存中
  294. CalcCache::addHasPerfUsers($data['TO_USER_ID'], $this->_periodNum);
  295. //加入到报单会员中
  296. $toInfo = CalcCache::getUserInfo($data['TO_USER_ID'], $this->_periodNum);
  297. CalcCache::addHasBDUsers($data['TO_USER_ID'], $this->_periodNum, [
  298. 'TO_USER_ID' => $data['TO_USER_ID'],
  299. 'USER_ID' => $data['USER_ID'],
  300. 'DEC_ID' => $data['DEC_ID'],
  301. //考虑可能会移网的情况
  302. 'REC_USER_ID' => $toInfo['REC_UID'] ?? '',
  303. 'CON_USER_ID' => $toInfo['CON_UID'] ?? '',
  304. 'DEC_AMOUNT' => $data['DEC_AMOUNT'],
  305. 'DEC_PV' => $data['DEC_PV'],
  306. ]);
  307. // 给上追加业绩
  308. $this->loopNetworkParentDo($data['TO_USER_ID'], function ($parent) use (&$data) {
  309. // 给上级会员追加业绩到缓存中
  310. // CalcCache::addUserPerf($parent['PARENT_UID'], $this->_periodNum, [
  311. // 'PV_'.$parent['LOCATION'].'L' => $data['DEC_PV'],
  312. // ]);
  313. // 给上级会员追加本期业绩到缓存中
  314. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  315. 'PV_' . $parent['LOCATION'] . 'L' => $data['DEC_PV'],
  316. 'PV_' . $parent['LOCATION'] . 'L_TOUCH' => $data['DEC_PV'],
  317. 'PV_' . $parent['LOCATION'] . 'L_' . $data['TYPE'] => $data['DEC_PV'],
  318. ]);
  319. // 把该会员加入到能拿到业绩的会员缓存中
  320. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  321. unset($parent);
  322. });
  323. //给推荐关系累计增加业绩
  324. $this->loopRelationParentDo($data['TO_USER_ID'], function ($parent) use (&$data) {
  325. // 给上级会员追加业绩到缓存中
  326. // CalcCache::addUserPerf($parent['PARENT_UID'], $this->_periodNum, [
  327. // 'PV_'.$parent['LOCATION'].'L' => $data['DEC_PV'],
  328. // ]);
  329. // 给上级会员追加本期业绩到缓存中
  330. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  331. 'PV_PSS' => $data['DEC_PV'],
  332. ]);
  333. // 把该会员加入到能拿到业绩的会员缓存中
  334. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  335. unset($parent);
  336. });
  337. // }
  338. // 写入业绩单表
  339. $decInfo = CalcCache::getUserInfo($data['USER_ID'], $this->_periodNum);
  340. $sn = PerfOrder::generateSN();
  341. $insertPerfOrderData[] = [
  342. 'ID' => SnowFake::instance()->generateId(),
  343. 'SN' => $sn,
  344. 'DEC_SN' => $data['DEC_SN'],
  345. 'DEC_TYPE' => strtoupper($data['TYPE']),
  346. 'DEC_STATUS' => PerfOrder::STATUS_NORMAL,
  347. 'USER_ID' => $data['TO_USER_ID'],
  348. 'LAST_REC_USER_NAME' => $toInfo['REC_USER_NAME'],
  349. 'LAST_REC_REAL_NAME' => $toInfo['REC_REAL_NAME'],
  350. 'LAST_DEC_LV' => $toInfo['DEC_LV'],
  351. 'LAST_EMP_LV' => $toInfo['EMP_LV'],
  352. 'LAST_STATUS' => $toInfo['STATUS'],
  353. 'PV' => $data['DEC_PV'],
  354. 'DEC_AMOUNT' => $data['DEC_AMOUNT'],
  355. 'LAST_SUB_COM_ID' => $toInfo['SUB_COM_ID'],
  356. 'LAST_PROVINCE' => $toInfo['PROVINCE'],
  357. 'LAST_CITY' => $toInfo['CITY'],
  358. 'LAST_COUNTY' => $toInfo['COUNTY'],
  359. 'DEC_USER_ID' => $data['USER_ID'],
  360. 'LAST_DEC_DEC_LV' => $decInfo['DEC_LV'],
  361. 'LAST_DEC_SUB_COM_ID' => $decInfo['SUB_COM_ID'],
  362. 'LAST_DEC_PROVINCE' => $decInfo['DEC_PROVINCE'],
  363. 'LAST_DEC_CITY' => $decInfo['DEC_CITY'],
  364. 'LAST_DEC_COUNTY' => $decInfo['DEC_COUNTY'],
  365. 'PERIOD_NUM' => $this->_periodNum,
  366. 'CALC_MONTH' => $this->_calcYearMonth,
  367. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  368. 'CREATED_AT' => Date::nowTime(),
  369. 'CLOSED_AT' => 0,
  370. ];
  371. unset($data, $decInfo, $sn, $toInfo);
  372. }
  373. PerfOrder::batchInsert($insertPerfOrderData);
  374. unset($insertPerfOrderData, $allData, $snArr);
  375. return $this->loopCalcPeriodPerfByDecOrder($offset + $this->_limit);
  376. }
  377. unset($allData);
  378. return true;
  379. }
  380. /**
  381. * 周结,向上级算业绩,并计入业绩单
  382. * @param int $offset
  383. * @return bool
  384. * @throws \yii\db\Exception
  385. */
  386. public function loopCalcPeriodPerfByOrderDec($offset = 0) {
  387. // 循环获取全部报单
  388. $allData = OrderDec::findUseDbCalc()->select('ID,SN,USER_ID,ORDER_TYPE,ORDER_AMOUNT,PV,PAY_AMOUNT,PAY_PV,PERIOD_NUM,PAY_TYPE')->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE", [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE'=>'ZC'])->orderBy('ID DESC')->offset($offset)->limit($this->_limit)->asArray()->all();
  389. if ($allData) {
  390. $insertPerfOrderData = [];
  391. foreach ($allData as $data) {
  392. // 是否关停等状态不能拿业绩
  393. if (!$this->isHasPerf($data['USER_ID'])) {
  394. continue;
  395. }
  396. // 给自己增加PCS(个人消费)
  397. CalcCache::nowPeriodPerf($data['USER_ID'], $this->_periodNum, [
  398. 'PV_PCS' => $data['PAY_PV'],
  399. 'PV_PCS_ZC' => $data['PAY_PV'],
  400. ]);
  401. // 把该会员加入到能拿到业绩的会员缓存中
  402. CalcCache::addHasPerfUsers($data['USER_ID'], $this->_periodNum);
  403. //加入到报单会员中
  404. $toInfo = CalcCache::getUserInfo($data['USER_ID'], $this->_periodNum);
  405. CalcCache::addHasBDUsers($data['USER_ID'], $this->_periodNum, [
  406. 'TO_USER_ID' => $data['USER_ID'],
  407. 'USER_ID' => $toInfo['DEC_ID'],
  408. 'DEC_ID' => $toInfo['DEC_ID'],
  409. //考虑可能会移网的情况
  410. 'REC_USER_ID' => $toInfo['REC_UID'] ?? '',
  411. 'CON_USER_ID' => $toInfo['CON_UID'] ?? '',
  412. 'DEC_AMOUNT' => $data['ORDER_AMOUNT'],
  413. 'DEC_PV' => $data['PAY_PV'],
  414. ]);
  415. // 给上追加业绩
  416. $this->loopNetworkParentDo($data['USER_ID'], function ($parent) use (&$data) {
  417. // 给上级会员追加本期业绩到缓存中
  418. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  419. 'PV_' . $parent['LOCATION'] . 'L' => $data['PAY_PV'],
  420. 'PV_' . $parent['LOCATION'] . 'L_TOUCH' => $data['PAY_PV'],
  421. 'PV_' . $parent['LOCATION'] . 'L_' . $data['ORDER_TYPE'] => $data['PAY_PV'],
  422. ]);
  423. // 把该会员加入到能拿到业绩的会员缓存中
  424. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  425. unset($parent);
  426. });
  427. //给推荐关系累计增加业绩
  428. $this->loopRelationParentDo($data['USER_ID'], function ($parent) use (&$data) {
  429. // 给上级会员追加本期业绩到缓存中
  430. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  431. 'PV_PSS' => $data['PAY_PV'],
  432. ]);
  433. // 把该会员加入到能拿到业绩的会员缓存中
  434. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  435. unset($parent);
  436. });
  437. // 写入业绩单表
  438. $decInfo = CalcCache::getUserInfo($toInfo['DEC_ID'], $this->_periodNum);
  439. $sn = PerfOrder::generateSN();
  440. $insertPerfOrderData[] = [
  441. 'ID' => SnowFake::instance()->generateId(),
  442. 'SN' => $sn,
  443. 'DEC_SN' => $data['SN'],
  444. 'DEC_TYPE' => strtoupper($data['ORDER_TYPE']),
  445. 'DEC_STATUS' => PerfOrder::STATUS_NORMAL,
  446. 'USER_ID' => $data['USER_ID'],
  447. 'LAST_REC_USER_NAME' => $toInfo['REC_USER_NAME'],
  448. 'LAST_REC_REAL_NAME' => $toInfo['REC_REAL_NAME'],
  449. 'LAST_DEC_LV' => $toInfo['DEC_LV'],
  450. 'LAST_EMP_LV' => $toInfo['EMP_LV'],
  451. 'LAST_STATUS' => $toInfo['STATUS'],
  452. 'PV' => $data['PAY_PV'],
  453. 'DEC_AMOUNT' => $data['ORDER_AMOUNT'],
  454. 'LAST_SUB_COM_ID' => $toInfo['SUB_COM_ID'],
  455. 'LAST_PROVINCE' => $toInfo['PROVINCE'],
  456. 'LAST_CITY' => $toInfo['CITY'],
  457. 'LAST_COUNTY' => $toInfo['COUNTY'],
  458. 'DEC_USER_ID' => $toInfo['DEC_ID'],
  459. 'LAST_DEC_DEC_LV' => $decInfo['DEC_LV'],
  460. 'LAST_DEC_SUB_COM_ID' => $decInfo['SUB_COM_ID'],
  461. 'LAST_DEC_PROVINCE' => $decInfo['DEC_PROVINCE'],
  462. 'LAST_DEC_CITY' => $decInfo['DEC_CITY'],
  463. 'LAST_DEC_COUNTY' => $decInfo['DEC_COUNTY'],
  464. 'PERIOD_NUM' => $this->_periodNum,
  465. 'CALC_MONTH' => $this->_calcYearMonth,
  466. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  467. 'CREATED_AT' => Date::nowTime(),
  468. 'CLOSED_AT' => 0,
  469. ];
  470. unset($data, $decInfo, $sn, $toInfo);
  471. }
  472. PerfOrder::batchInsert($insertPerfOrderData);
  473. unset($insertPerfOrderData, $allData, $snArr);
  474. return $this->loopCalcPeriodPerfByOrderDec($offset + $this->_limit);
  475. }
  476. unset($allData);
  477. return true;
  478. }
  479. /**
  480. * 从会员的复销订单会员计算复销业绩并加入业绩单
  481. * @param int $offset
  482. * @return bool
  483. * @throws \yii\db\Exception
  484. */
  485. public function loopCalcPerfByFXOrder(int $offset = 0) {
  486. // 循环获取全部报单
  487. $allData = Order::findUseDbCalc()->select('ID,SN,DEC_SN,USER_ID,ORDER_TYPE,ORDER_AMOUNT,PAY_AMOUNT,PAY_PV,PAY_TYPE,PERIOD_NUM,STATUS,IS_DELETE,P_CALC_MONTH,CREATED_AT')->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE", [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE'=>DeclarationForm::TYPE_FX])->orderBy('CREATED_AT DESC,ID DESC')->offset($offset)->limit($this->_limit)->asArray()->all();
  488. if ($allData) {
  489. $insertPerfOrderData = [];
  490. foreach ($allData as $data) {
  491. // 是否关停等状态不能拿业绩
  492. if (!$this->isHasPerf($data['USER_ID'])) {
  493. continue;
  494. }
  495. //如果支付方式是现金,那么实际业绩是支付PV的50%
  496. if( $data['PAY_TYPE'] === self::ORDER_PAY_TYPE_CASH ) {
  497. $orderCashAmount = $data['ORDER_AMOUNT'];
  498. //111期开始由50%改为60%-by 2020-04-30修改
  499. $payPv = $data['PAY_PV'] * $this->_sysConfig['cashReconsumeBonusPercent']['VALUE'] / 100;
  500. $cacheDataKey = 'PV_PCS_FX_CASH';
  501. }else {
  502. $orderCashAmount = 0;
  503. $payPv = $data['PAY_PV'];
  504. $cacheDataKey = 'PV_PCS_FX_POINT';
  505. }
  506. if( $payPv <= 0 ) continue;
  507. // 给自己增加PCS(个人消费)
  508. CalcCache::nowPeriodPerf($data['USER_ID'], $this->_periodNum, [
  509. 'FX_AMOUNT_CASH' => $orderCashAmount,
  510. 'PV_PCS' => $payPv,
  511. 'PV_PCS_FX' => $payPv,
  512. $cacheDataKey => $payPv,
  513. ]);
  514. // 把该会员加入到能拿到业绩的会员缓存中
  515. CalcCache::addHasPerfUsers($data['USER_ID'], $this->_periodNum);
  516. // 给上追加业绩
  517. try {
  518. $this->loopNetworkParentDo($data['USER_ID'], function ($parent) use (&$data, $payPv) {
  519. // 给上级会员追加业绩到缓存中
  520. // CalcCache::addUserPerf($parent['PARENT_UID'], $this->_periodNum, [
  521. // 'PV_'.$parent['LOCATION'].'L' => $data['DEC_PV'],
  522. // ]);
  523. // 给上级会员追加本期业绩到缓存中
  524. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  525. 'PV_' . $parent['LOCATION'] . 'L' => $payPv,
  526. 'PV_' . $parent['LOCATION'] . 'L_TOUCH' => $payPv,
  527. 'PV_' . $parent['LOCATION'] . 'L_FX' => $payPv,
  528. ]);
  529. // 把该会员加入到能拿到业绩的会员缓存中
  530. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  531. });
  532. } catch(\Exception $e) {
  533. file_put_contents('loopNetworkParentDo_error.txt', var_export([
  534. 'USER_ID' => $data['USER_ID'],
  535. '_periodNum' => $this->_periodNum,
  536. 'error' => $e->getMessage()
  537. ],true));
  538. }
  539. //给推荐关系累计增加业绩
  540. $this->loopRelationParentDo($data['USER_ID'], function ($parent) use ($data, $payPv) {
  541. // 给上级会员追加业绩到缓存中
  542. // CalcCache::addUserPerf($parent['PARENT_UID'], $this->_periodNum, [
  543. // 'PV_'.$parent['LOCATION'].'L' => $data['DEC_PV'],
  544. // ]);
  545. // 给上级会员追加本期业绩到缓存中
  546. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  547. 'PV_PSS' => $payPv,
  548. ]);
  549. // 把该会员加入到能拿到业绩的会员缓存中
  550. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  551. });
  552. // }
  553. // 写入业绩单表
  554. $baseInfo = CalcCache::getUserInfo($data['USER_ID'], $this->_periodNum);
  555. $sn = PerfOrder::generateSN();
  556. $insertPerfOrderData[] = [
  557. 'ID' => SnowFake::instance()->generateId(),
  558. 'SN' => $sn,
  559. 'DEC_SN' => null,
  560. 'DEC_TYPE' => 'FX',
  561. 'DEC_STATUS' => PerfOrder::STATUS_NORMAL,
  562. 'USER_ID' => $data['USER_ID'],
  563. 'LAST_REC_USER_NAME' => $baseInfo['REC_USER_NAME'],
  564. 'LAST_REC_REAL_NAME' => $baseInfo['REC_REAL_NAME'],
  565. 'LAST_DEC_LV' => $baseInfo['DEC_LV'],
  566. 'LAST_EMP_LV' => $baseInfo['EMP_LV'],
  567. 'LAST_STATUS' => $baseInfo['STATUS'],
  568. 'PV' => $payPv,
  569. 'DEC_AMOUNT' => $data['PAY_AMOUNT'],
  570. 'LAST_SUB_COM_ID' => $baseInfo['SUB_COM_ID'],
  571. 'LAST_PROVINCE' => $baseInfo['PROVINCE'],
  572. 'LAST_CITY' => $baseInfo['CITY'],
  573. 'LAST_COUNTY' => $baseInfo['COUNTY'],
  574. 'DEC_USER_ID' => $data['USER_ID'],
  575. 'LAST_DEC_DEC_LV' => $baseInfo['DEC_LV'],
  576. 'LAST_DEC_SUB_COM_ID' => $baseInfo['SUB_COM_ID'],
  577. 'LAST_DEC_PROVINCE' => $baseInfo['PROVINCE'],
  578. 'LAST_DEC_CITY' => $baseInfo['CITY'],
  579. 'LAST_DEC_COUNTY' => $baseInfo['COUNTY'],
  580. 'PERIOD_NUM' => $this->_periodNum,
  581. 'CALC_MONTH' => $this->_calcYearMonth,
  582. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  583. 'CREATED_AT' => Date::nowTime(),
  584. 'CLOSED_AT' => 0,
  585. ];
  586. unset($data, $baseInfo, $sn, $orderCashAmount, $payPv, $cacheDataKey);
  587. }
  588. PerfOrder::batchInsert($insertPerfOrderData);
  589. unset($insertPerfOrderData, $allData, $snArr);
  590. return $this->loopCalcPerfByFXOrder($offset + $this->_limit);
  591. }
  592. unset($allData);
  593. return true;
  594. }
  595. /**
  596. * 从会员的商城复销订单会员计算复销业绩并加入业绩单
  597. * @param int $offset
  598. * @return bool
  599. * @throws \yii\db\Exception
  600. */
  601. public function loopCalcPerfByShopFXOrder(int $offset = 0) {
  602. // 循环获取全部报单
  603. $allData = OrderShop::findUseDbCalc()->select('ID,SN,DEC_SN,USER_ID,ORDER_TYPE,ORDER_AMOUNT,PAY_AMOUNT,PAY_PV,PAY_TYPE,PERIOD_NUM,STATUS,IS_DELETE,P_CALC_MONTH,CREATED_AT')->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE", [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE'=>DeclarationForm::TYPE_FX])->orderBy('CREATED_AT DESC,ID DESC')->offset($offset)->limit($this->_limit)->asArray()->all();
  604. if ($allData) {
  605. $insertPerfOrderData = [];
  606. foreach ($allData as $data) {
  607. // 是否关停等状态不能拿业绩
  608. if (!$this->isHasPerf($data['USER_ID'])) {
  609. continue;
  610. }
  611. //如果支付方式是现金,那么实际业绩是支付PV的50%
  612. if( $data['PAY_TYPE'] === self::ORDER_PAY_TYPE_CASH ) {
  613. $orderCashAmount = $data['ORDER_AMOUNT'];
  614. //111期开始由50%改为60%-by 2020-04-30修改
  615. $payPv = $data['PAY_PV'] * $this->_sysConfig['cashReconsumeBonusPercent']['VALUE'] / 100;
  616. $cacheDataKey = 'PV_PCS_FX_CASH';
  617. }else {
  618. $orderCashAmount = 0;
  619. $payPv = $data['PAY_PV'];
  620. $cacheDataKey = 'PV_PCS_FX_POINT';
  621. }
  622. if( $payPv <= 0 ) continue;
  623. // 给自己增加PCS(个人消费)
  624. CalcCache::nowPeriodPerf($data['USER_ID'], $this->_periodNum, [
  625. 'FX_AMOUNT_CASH' => $orderCashAmount,
  626. 'PV_PCS' => $payPv,
  627. 'PV_PCS_FX' => $payPv,
  628. $cacheDataKey => $payPv,
  629. ]);
  630. // 把该会员加入到能拿到业绩的会员缓存中
  631. CalcCache::addHasPerfUsers($data['USER_ID'], $this->_periodNum);
  632. // 给上追加业绩
  633. $this->loopNetworkParentDo($data['USER_ID'], function ($parent) use (&$data, $payPv) {
  634. // 给上级会员追加业绩到缓存中
  635. // CalcCache::addUserPerf($parent['PARENT_UID'], $this->_periodNum, [
  636. // 'PV_'.$parent['LOCATION'].'L' => $data['DEC_PV'],
  637. // ]);
  638. // 给上级会员追加本期业绩到缓存中
  639. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  640. 'PV_' . $parent['LOCATION'] . 'L' => $payPv,
  641. 'PV_' . $parent['LOCATION'] . 'L_TOUCH' => $payPv,
  642. 'PV_' . $parent['LOCATION'] . 'L_FX' => $payPv,
  643. ]);
  644. // 把该会员加入到能拿到业绩的会员缓存中
  645. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  646. });
  647. //给推荐关系累计增加业绩
  648. $this->loopRelationParentDo($data['USER_ID'], function ($parent) use ($data, $payPv) {
  649. // 给上级会员追加业绩到缓存中
  650. // CalcCache::addUserPerf($parent['PARENT_UID'], $this->_periodNum, [
  651. // 'PV_'.$parent['LOCATION'].'L' => $data['DEC_PV'],
  652. // ]);
  653. // 给上级会员追加本期业绩到缓存中
  654. CalcCache::nowPeriodPerf($parent['PARENT_UID'], $this->_periodNum, [
  655. 'PV_PSS' => $payPv,
  656. ]);
  657. // 把该会员加入到能拿到业绩的会员缓存中
  658. CalcCache::addHasPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  659. });
  660. // }
  661. // 写入业绩单表
  662. $baseInfo = CalcCache::getUserInfo($data['USER_ID'], $this->_periodNum);
  663. $sn = PerfOrder::generateSN();
  664. $insertPerfOrderData[] = [
  665. 'ID' => SnowFake::instance()->generateId(),
  666. 'SN' => $sn,
  667. 'DEC_SN' => null,
  668. 'DEC_TYPE' => 'FX',
  669. 'DEC_STATUS' => PerfOrder::STATUS_NORMAL,
  670. 'USER_ID' => $data['USER_ID'],
  671. 'LAST_REC_USER_NAME' => $baseInfo['REC_USER_NAME'],
  672. 'LAST_REC_REAL_NAME' => $baseInfo['REC_REAL_NAME'],
  673. 'LAST_DEC_LV' => $baseInfo['DEC_LV'],
  674. 'LAST_EMP_LV' => $baseInfo['EMP_LV'],
  675. 'LAST_STATUS' => $baseInfo['STATUS'],
  676. 'PV' => $payPv,
  677. 'DEC_AMOUNT' => $data['PAY_AMOUNT'],
  678. 'LAST_SUB_COM_ID' => $baseInfo['SUB_COM_ID'],
  679. 'LAST_PROVINCE' => $baseInfo['PROVINCE'],
  680. 'LAST_CITY' => $baseInfo['CITY'],
  681. 'LAST_COUNTY' => $baseInfo['COUNTY'],
  682. 'DEC_USER_ID' => $data['USER_ID'],
  683. 'LAST_DEC_DEC_LV' => $baseInfo['DEC_LV'],
  684. 'LAST_DEC_SUB_COM_ID' => $baseInfo['SUB_COM_ID'],
  685. 'LAST_DEC_PROVINCE' => $baseInfo['PROVINCE'],
  686. 'LAST_DEC_CITY' => $baseInfo['CITY'],
  687. 'LAST_DEC_COUNTY' => $baseInfo['COUNTY'],
  688. 'PERIOD_NUM' => $this->_periodNum,
  689. 'CALC_MONTH' => $this->_calcYearMonth,
  690. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  691. 'CREATED_AT' => Date::nowTime(),
  692. 'CLOSED_AT' => 0,
  693. ];
  694. unset($data, $baseInfo, $sn, $orderCashAmount, $payPv, $cacheDataKey);
  695. }
  696. PerfOrder::batchInsert($insertPerfOrderData);
  697. unset($insertPerfOrderData, $allData, $snArr);
  698. return $this->loopCalcPerfByShopFXOrder($offset + $this->_limit);
  699. }
  700. unset($allData);
  701. return true;
  702. }
  703. /**
  704. * 达标复销订单
  705. * @param int $offset
  706. * @return bool
  707. * @throws \yii\db\Exception
  708. */
  709. public function loopCalcPerfByStandardFXOrder(int $offset = 0) {
  710. if (!$this->_isCalcMonth) {
  711. return true;
  712. }
  713. $allData = OrderStandard::findUseDbCalc()->select('ID,SN,DEC_SN,USER_ID,ORDER_TYPE,ORDER_AMOUNT,PAY_AMOUNT,PAY_PV,PAY_TYPE,PERIOD_NUM,STATUS,IS_DELETE,CREATED_AT')->where("CALC_MONTH=:CALC_MONTH", [':CALC_MONTH' => $this->_calcYearMonth])->orderBy('ID DESC')->offset($offset)->limit($this->_limit)->asArray()->all();
  714. if ($allData) {
  715. $insertPerfOrderData = [];
  716. foreach ($allData as $data) {
  717. // 是否关停等状态不能拿业绩
  718. if (!$this->isHasPerf($data['USER_ID'])) {
  719. continue;
  720. }
  721. //如果支付方式是现金,那么实际业绩是支付PV的50%
  722. if( $data['PAY_TYPE'] === self::ORDER_PAY_TYPE_CASH ) {
  723. $orderCashAmount = $data['ORDER_AMOUNT'];
  724. // $payPv = $data['PAY_PV'] * $this->_sysConfig['cashReconsumeBonusPercent']['VALUE'] / 100;
  725. }else {
  726. $orderCashAmount = 0;
  727. // $payPv = $data['PAY_PV'];
  728. }
  729. if( $orderCashAmount <= 0 ) continue;
  730. // 给自己增加个人业绩
  731. CalcCache::nowStandardMonthPerf($data['USER_ID'], $this->_periodNum, [
  732. 'AMOUNT_PCS' => $orderCashAmount,
  733. ]);
  734. // 把该会员加入到能拿到业绩的会员缓存中
  735. CalcCache::addHasStandardMonthPerfUsers($data['USER_ID'], $this->_periodNum);
  736. //只有无聘级业绩才向上累加
  737. $baseInfo = CalcCache::getUserInfo($data['USER_ID'], $this->_periodNum);
  738. if( $baseInfo['EMP_LV'] != EmployLevel::NO_LEVEL_ID ) {
  739. continue;
  740. }
  741. //给推荐团队添加团队业绩 注:如果些订单特别多的情况,可以分2步。先只计算个人业绩、然后根据个人业绩再计算团队业绩。
  742. //给推荐关系累计增加业绩
  743. $this->loopRelationParentDo($data['USER_ID'], function ($parent) use ($data, $orderCashAmount) {
  744. // 给上级会员追加本期业绩到缓存中
  745. CalcCache::nowStandardMonthPerf($parent['PARENT_UID'], $this->_periodNum, [
  746. 'AMOUNT_PSS' => $orderCashAmount,
  747. ]);
  748. // 把该会员加入到能拿到业绩的会员缓存中
  749. CalcCache::addHasStandardMonthPerfUsers($parent['PARENT_UID'], $this->_periodNum);
  750. //获取parent聘级
  751. $parentUserInfo = CalcCache::getUserInfo($parent['PARENT_UID'], $this->_periodNum);
  752. if( $parentUserInfo['EMP_LV'] != EmployLevel::NO_LEVEL_ID ) {//如果碰到有聘级的业绩就不在向上累加了
  753. return self::LOOP_FINISH;
  754. }
  755. });
  756. unset($data, $baseInfo, $orderCashAmount);
  757. }
  758. unset($insertPerfOrderData, $allData, $snArr);
  759. return $this->loopCalcPerfByStandardFXOrder($offset + $this->_limit);
  760. }
  761. unset($allData);
  762. return true;
  763. }
  764. /**
  765. * 计算月业绩表相关的数据并写入数据库
  766. * @param int $offset
  767. * @return bool
  768. * @throws Exception
  769. * @throws \yii\db\Exception
  770. */
  771. public function loopCalcMonthPerfTableData(int $offset = 0) {
  772. if (!$this->_isCalcMonth) {
  773. return true;
  774. }
  775. echo sprintf("时间:[%s]月业绩,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  776. // 从缓存列表里面从底层往上倒序获取会员
  777. // $allData = PerfPeriod::findUseDbCalc()->from(PerfPeriod::tableName() . ' AS PP')->select('PP.USER_ID AS USER_ID, SUM(PP.PV_PCS) AS PV_PCS_SUM,SUM(PP.PV_PSS) AS PV_PSS_SUM,SUM(PP.PV_1L) AS PV_1L_SUM,SUM(PP.PV_2L) AS PV_2L_SUM,SUM(PP.PV_3L) AS PV_3L_SUM,SUM(PP.PV_4L) AS PV_4L_SUM,SUM(PP.PV_5L) AS PV_5L_SUM, SUM(PM.PV_1L_TOTAL) AS PV_1L_TOTAL, SUM(PM.PV_2L_TOTAL) AS PV_2L_TOTAL, SUM(PM.PV_3L_TOTAL) AS PV_3L_TOTAL, SUM(PM.PV_4L_TOTAL) AS PV_4L_TOTAL,SUM(PM.PV_5L_TOTAL) AS PV_5L_TOTAL, SUM(PM.PV_PSS_TOTAL) AS PV_PSS_TOTAL')->leftJoin(PerfMonth::tableName() . ' AS PM', 'PM.USER_ID=PP.USER_ID AND PM.CALC_MONTH=:LAST_CALC_MONTH', ['LAST_CALC_MONTH'=>$this->_lastCalcYearMonth])->where('PP.CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])->groupBy('PP.USER_ID')->orderBy('PP.USER_ID DESC')->offset($offset)->limit($this->_limit)->asArray()->all();
  778. $allData = PerfPeriod::findUseDbCalc()->select('USER_ID, SUM(FX_AMOUNT_CASH) AS FX_AMOUNT_CASH_SUM,SUM(PV_PCS) AS PV_PCS_SUM,SUM(PV_PCS_FX) AS PV_PCS_FX_SUM,SUM(PV_PSS) AS PV_PSS_SUM,SUM(PV_1L) AS PV_1L_SUM,SUM(PV_2L) AS PV_2L_SUM,SUM(PV_3L) AS PV_3L_SUM,SUM(PV_4L) AS PV_4L_SUM,SUM(PV_5L) AS PV_5L_SUM,SUM(PV_1L_ZC) AS PV_1L_ZC_SUM,SUM(PV_2L_ZC) AS PV_2L_ZC_SUM,SUM(PV_3L_ZC) AS PV_3L_ZC_SUM,SUM(PV_4L_ZC) AS PV_4L_ZC_SUM,SUM(PV_5L_ZC) AS PV_5L_ZC_SUM')->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])->groupBy('USER_ID')->orderBy('USER_ID DESC')->offset($offset)->limit($this->_limit)->asArray()->all();
  779. if ($allData) {
  780. // 月度业绩表
  781. foreach ($allData as $everyData) {
  782. $userId = $everyData['USER_ID'];
  783. // $lastMonthData = PerfMonth::find()->select('PV_1L_TOTAL, PV_2L_TOTAL, PV_3L_TOTAL, PV_4L_TOTAL, PV_5L_TOTAL, PV_PSS_TOTAL')->where('USER_ID=:USER_ID AND CALC_MONTH=:LAST_CALC_MONTH', [
  784. // 'USER_ID'=>$userId,
  785. // 'LAST_CALC_MONTH'=>$this->_lastCalcYearMonth,
  786. // ])->asArray()->one();
  787. //往期业绩
  788. $userLastPerf = CalcCache::userPerf($userId, $this->_periodNum);
  789. //本期业绩
  790. $periodPerf = CalcCache::nowPeriodPerf($userId, $this->_periodNum);
  791. $userBaseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  792. //级别必须为VIP
  793. $isVip = false;
  794. if( $userBaseInfo['DEC_LV'] === DeclarationLevel::VIP_LEVEL_ID ) {
  795. $isVip= true;
  796. }
  797. if( $this->_sysConfig['vipBonusGoldDecLevel']['VALUE'] && $userBaseInfo['DEC_LV'] === DeclarationLevel::JIN_ZUAN_LEVEL_ID ) {
  798. $isVip = true;
  799. }
  800. $nowMonthPerf = [
  801. 'USER_ID' => $userId,
  802. 'FX_AMOUNT_CASH' => $everyData['FX_AMOUNT_CASH_SUM'],
  803. 'PV_PCS' => $everyData['PV_PCS_SUM'],
  804. 'PV_PCS_FX' => $everyData['PV_PCS_FX_SUM'],
  805. 'PV_PSS' => $everyData['PV_PSS_SUM'],
  806. 'PV_1L' => $everyData['PV_1L_SUM'],
  807. 'PV_2L' => $everyData['PV_2L_SUM'],
  808. 'PV_3L' => $everyData['PV_3L_SUM'],
  809. 'PV_4L' => $everyData['PV_4L_SUM'],
  810. 'PV_5L' => $everyData['PV_5L_SUM'],
  811. //VIP统计相关业绩
  812. 'VIP_PV_1L_ZC' => $isVip ? $everyData['PV_1L_ZC_SUM'] : 0,
  813. 'VIP_PV_2L_ZC' => $isVip ? $everyData['PV_2L_ZC_SUM'] : 0,
  814. 'VIP_PV_3L_ZC' => $isVip ? $everyData['PV_3L_ZC_SUM'] : 0,
  815. 'VIP_PV_4L_ZC' => $isVip ? $everyData['PV_4L_ZC_SUM'] : 0,
  816. 'VIP_PV_5L_ZC' => $isVip ? $everyData['PV_5L_ZC_SUM'] : 0,
  817. //总数据,历史+本期。不能用上月加本月,因为上月可能没业绩,上上个月有业绩。
  818. 'PV_1L_TOTAL' => $periodPerf['PV_1L'] + $userLastPerf['PV_1L'],
  819. 'PV_2L_TOTAL' => $periodPerf['PV_2L'] + $userLastPerf['PV_2L'],
  820. 'PV_3L_TOTAL' => $periodPerf['PV_3L'] + $userLastPerf['PV_3L'],
  821. 'PV_4L_TOTAL' => $periodPerf['PV_4L'] + $userLastPerf['PV_4L'],
  822. 'PV_5L_TOTAL' => $periodPerf['PV_5L'] + $userLastPerf['PV_5L'],
  823. 'PV_PSS_TOTAL' => $periodPerf['PV_PSS'] + $userLastPerf['PV_PSS_TOTAL'],
  824. ];
  825. // 把会员的月业绩写入缓存中,以便下面的奖金计算从缓冲中获取数据效率高
  826. CalcCache::addHasMonthPerfUsers($userId, $this->_periodNum);
  827. CalcCache::nowMonthPerf($userId, $this->_periodNum, $nowMonthPerf);
  828. unset($userId, $everyData, $nowMonthPerf, $lastMonthData, $userBaseInfo, $isVip);
  829. }
  830. unset($allData);
  831. $this->loopCalcMonthPerfTableData($offset + $this->_limit);
  832. }
  833. unset($allData);
  834. return true;
  835. }
  836. /**
  837. * 计算聘级
  838. * @param int $offset
  839. * @return bool
  840. * @throws Exception
  841. * @throws \yii\db\Exception
  842. */
  843. public function loopCalcEmpLevel(int $offset = 0) {
  844. if (!$this->_isCalcMonth) {
  845. return true;
  846. }
  847. echo sprintf("时间:[%s]计算聘级,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  848. // 从缓存列表里面从底层往上倒序获取会员
  849. $allData = CalcCache::getUsers($this->_periodNum, $offset, $this->_limit);
  850. if ($allData) {
  851. // 月度业绩表
  852. foreach ($allData as $userId) {
  853. $userInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  854. $nowMonthPerf = CalcCache::nowMonthPerf($userId, $this->_periodNum);
  855. $empLevel = $this->checkEmpLevel($userId, $nowMonthPerf, $userInfo['EMP_LV']);
  856. //根据用户的级别判断 能否得到级别积分
  857. if( $empLevel['LEVEL_SCORE'] > 0 ) {
  858. CalcCache::nowMonthScore($userId, $this->_periodNum, [
  859. 'LEVEL_SCORE' => $empLevel['LEVEL_SCORE'],
  860. ]);
  861. CalcCache::addHasScoreUsers($userId, $this->_periodNum);
  862. }
  863. //更新月业绩的聘级和用户信息中的聘级
  864. CalcCache::nowMonthPerf($userId, $this->_periodNum, [
  865. 'EMP_LEVEL' => $empLevel['ID']
  866. ]);
  867. //为业绩单更新结算时聘级
  868. // PerfOrder::updateAll(['LAST_EMP_LV'=>$empLevel['ID']],'USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM',[':USER_ID'=>$userId,':PERIOD_NUM'=>$this->_periodNum]);
  869. $userEmpLevel = $this->_empLevelConfig[$userInfo['EMP_LV']];
  870. $userEmpLevelSort = $userEmpLevel['SORT'] ?? EmployLevel::EMP_LEVEL_SORT['NO_LEVEL'];
  871. //不降级
  872. if( $empLevel['SORT'] <= $userEmpLevelSort ) continue;
  873. $userInfo['EMP_LV'] = $empLevel['ID'];
  874. CalcCache::setUserInfo($userId, $this->_periodNum, $userInfo);
  875. //可以判断用户是否升级,能否得到升级积分
  876. //因为默认是不降级,直接获得升级积分
  877. $upgradeScore = 0;
  878. foreach ($this->_empLevelConfig as $everyEmpLevel) {
  879. if( $everyEmpLevel['SORT'] <= $userEmpLevelSort ) continue;
  880. if( $everyEmpLevel['SORT'] > $empLevel['SORT'] ) continue;
  881. $upgradeScore += $everyEmpLevel['UPGRADE_SCORE'];
  882. unset($everyEmpLevel);
  883. }
  884. if( $upgradeScore > 0 ) {
  885. CalcCache::nowMonthScore($userId, $this->_periodNum, [
  886. 'UPGRADE_SCORE' => $upgradeScore
  887. ]);
  888. CalcCache::addHasScoreUsers($userId, $this->_periodNum);
  889. }
  890. unset($userId, $empLevel, $nowMonthPerf, $userInfo, $userEmpLevel, $userEmpLevelSort, $upgradeScore);
  891. }
  892. unset($allData);
  893. $this->loopCalcEmpLevel($offset + $this->_limit);
  894. }
  895. unset($allData);
  896. return true;
  897. }
  898. /**
  899. * 计算公司总业绩
  900. * @return bool
  901. * @throws Exception
  902. */
  903. public function calcPerfCompany() {
  904. if (!$this->_isCalcMonth) {
  905. return true;
  906. }
  907. $perfCompany = PerfCompany::findOne(['CALC_MONTH' => $this->_calcYearMonth]);
  908. $db = \Yii::$app->db;
  909. $transaction = $db->beginTransaction();
  910. try {
  911. if (!$perfCompany) {
  912. $perfCompany = new PerfCompany();
  913. }
  914. $perfCompany->PV = $this->_companyMonthPerf;
  915. $perfCompany->CREATED_AT = Date::nowTime();
  916. $perfCompany->CALC_YEAR = $this->_calcYear;
  917. $perfCompany->CALC_MONTH = $this->_calcYearMonth;
  918. if (!$perfCompany->save()) {
  919. throw new Exception(Form::formatErrorsForApi($perfCompany->getErrors()));
  920. }
  921. $transaction->commit();
  922. } catch (Exception $e) {
  923. $transaction->rollBack();
  924. throw new Exception($e->getMessage());
  925. }
  926. unset($perfCompany);
  927. return true;
  928. }
  929. /**
  930. * 循环有业绩会员,并入库
  931. * @param int $offset
  932. * @return bool
  933. * @throws \yii\db\Exception
  934. */
  935. public function loopWriteNowPerf($offset = 0) {
  936. echo sprintf("时间:[%s]缓存本期业绩数据入库,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  937. // 从缓存列表里面从底层往上倒序获取会员
  938. $allData = CalcCache::getHasPerfUsers($this->_periodNum, $offset, $this->_limit);
  939. if($allData){
  940. $insertDataPeriodPerf = [];
  941. foreach($allData as $userId){
  942. $insertDataPeriodPerf[] = $this->nowPeriodPerfData($userId);
  943. unset($userId);
  944. }
  945. PerfPeriod::batchInsert($insertDataPeriodPerf);
  946. unset($insertDataPeriodPerf, $allData);
  947. return $this->loopWriteNowPerf($offset + $this->_limit);
  948. }
  949. unset($allData);
  950. return true;
  951. }
  952. /**
  953. * 循环有月业绩会员,并入库
  954. * @param int $offset
  955. * @return bool
  956. * @throws \yii\db\Exception
  957. */
  958. public function loopWriteMonthPerf($offset = 0) {
  959. if(!$this->_isCalcMonth){
  960. return true;
  961. }
  962. echo sprintf("时间:[%s]缓存本月业绩数据入库,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  963. // 从缓存列表里面从底层往上倒序获取会员
  964. $allData = CalcCache::getHasMonthPerfUsers($this->_periodNum, $offset, $this->_limit);
  965. if($allData){
  966. $insertDataMonthPerf = [];
  967. foreach($allData as $userId){
  968. $insertDataMonthPerf[] = $this->nowMonthPerfData($userId);
  969. unset($userId);
  970. }
  971. PerfMonth::batchInsert($insertDataMonthPerf);
  972. unset($insertDataMonthPerf, $allData);
  973. return $this->loopWriteMonthPerf($offset + $this->_limit);
  974. }
  975. unset($allData);
  976. return true;
  977. }
  978. /**
  979. * 循环达标业绩会员,并入库
  980. * @param int $offset
  981. * @return bool
  982. * @throws \yii\db\Exception
  983. */
  984. public function loopWriteStandardPerf($offset = 0) {
  985. if(!$this->_isCalcMonth){
  986. return true;
  987. }
  988. echo sprintf("时间:[%s]缓存达标业绩数据入库,当前offset为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()) , $offset);
  989. // 从缓存列表里面从底层往上倒序获取会员
  990. $allData = CalcCache::getHasStandardMonthPerfUsers($this->_periodNum, $offset, $this->_limit);
  991. if($allData){
  992. $insertDataStandardPerf = [];
  993. foreach($allData as $userId) {
  994. $data = CalcCache::nowStandardMonthPerf($userId, $this->_periodNum);
  995. $baseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  996. $insertDataStandardPerf[] = [
  997. 'ID' => SnowFake::instance()->generateId(),
  998. 'USER_ID' => $userId,
  999. 'LAST_DEC_LV' => $baseInfo['DEC_LV'],
  1000. 'LAST_EMP_LV' => $baseInfo['EMP_LV'],
  1001. 'LAST_STATUS' => $baseInfo['STATUS'],
  1002. 'AMOUNT_PCS' => $data['AMOUNT_PCS'],
  1003. 'AMOUNT_PSS' => $data['AMOUNT_PSS'],
  1004. 'CALC_MONTH' => $this->_calcYearMonth,
  1005. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  1006. 'CREATED_AT' => Date::nowTime(),
  1007. ];
  1008. unset($data, $baseInfo, $userId);
  1009. }
  1010. PerfStandard::batchInsert($insertDataStandardPerf);
  1011. unset($insertDataStandardPerf, $allData);
  1012. return $this->loopWriteStandardPerf($offset + $this->_limit);
  1013. }
  1014. unset($allData);
  1015. return true;
  1016. }
  1017. /**
  1018. * 本期业绩数据
  1019. * @param $userId
  1020. * @return array
  1021. */
  1022. public function nowPeriodPerfData($userId){
  1023. $data = CalcCache::nowPeriodPerf($userId, $this->_periodNum);
  1024. $baseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  1025. $result = [
  1026. 'ID' => SnowFake::instance()->generateId(),
  1027. 'USER_ID' => $userId,
  1028. 'LAST_DEC_LV' => $baseInfo['DEC_LV'],
  1029. 'LAST_EMP_LV' => $baseInfo['EMP_LV'],
  1030. 'LAST_STATUS' => $baseInfo['STATUS'],
  1031. 'FX_AMOUNT_CASH' => $data['FX_AMOUNT_CASH'],
  1032. 'PV_PCS' => $data['PV_PCS'],
  1033. 'PV_PSS' => $data['PV_PSS'],
  1034. 'PV_PCS_ZC' => $data['PV_PCS_ZC'],
  1035. 'PV_PCS_YH' => $data['PV_PCS_YH'],
  1036. 'PV_PCS_ZG' => $data['PV_PCS_ZG'],
  1037. 'PV_PCS_LS' => $data['PV_PCS_LS'],
  1038. 'PV_PCS_FX' => $data['PV_PCS_FX'],
  1039. 'PV_PCS_FX_CASH' => $data['PV_PCS_FX_CASH'],
  1040. 'PV_PCS_FX_POINT' => $data['PV_PCS_FX_POINT'],
  1041. 'PV_1L' => $data['PV_1L'],
  1042. 'PV_1L_TOUCH' => $data['PV_1L_TOUCH'],
  1043. 'PV_1L_ZC' => $data['PV_1L_ZC'],
  1044. 'PV_1L_YH' => $data['PV_1L_YH'],
  1045. 'PV_1L_ZG' => $data['PV_1L_ZG'],
  1046. 'PV_1L_LS' => $data['PV_1L_LS'],
  1047. 'PV_1L_FX' => $data['PV_1L_FX'],
  1048. 'PV_2L' => $data['PV_2L'],
  1049. 'PV_2L_TOUCH' => $data['PV_2L_TOUCH'],
  1050. 'PV_2L_ZC' => $data['PV_2L_ZC'],
  1051. 'PV_2L_YH' => $data['PV_2L_YH'],
  1052. 'PV_2L_ZG' => $data['PV_2L_ZG'],
  1053. 'PV_2L_LS' => $data['PV_2L_LS'],
  1054. 'PV_2L_FX' => $data['PV_2L_FX'],
  1055. 'PV_3L' => $data['PV_3L'],
  1056. 'PV_3L_TOUCH' => $data['PV_3L_TOUCH'],
  1057. 'PV_3L_ZC' => $data['PV_3L_ZC'],
  1058. 'PV_3L_YH' => $data['PV_3L_YH'],
  1059. 'PV_3L_ZG' => $data['PV_3L_ZG'],
  1060. 'PV_3L_LS' => $data['PV_3L_LS'],
  1061. 'PV_3L_FX' => $data['PV_3L_FX'],
  1062. 'PV_4L' => $data['PV_4L'],
  1063. 'PV_4L_TOUCH' => $data['PV_4L_TOUCH'],
  1064. 'PV_4L_ZC' => $data['PV_4L_ZC'],
  1065. 'PV_4L_YH' => $data['PV_4L_YH'],
  1066. 'PV_4L_ZG' => $data['PV_4L_ZG'],
  1067. 'PV_4L_LS' => $data['PV_4L_LS'],
  1068. 'PV_4L_FX' => $data['PV_4L_FX'],
  1069. 'PV_5L' => $data['PV_5L'],
  1070. 'PV_5L_TOUCH' => $data['PV_5L_TOUCH'],
  1071. 'PV_5L_ZC' => $data['PV_5L_ZC'],
  1072. 'PV_5L_YH' => $data['PV_5L_YH'],
  1073. 'PV_5L_ZG' => $data['PV_5L_ZG'],
  1074. 'PV_5L_LS' => $data['PV_5L_LS'],
  1075. 'PV_5L_FX' => $data['PV_5L_FX'],
  1076. 'PV_LS_TOUCH' => $data['PV_LS_TOUCH'],
  1077. 'SURPLUS_1L' => $data['SURPLUS_1L'],
  1078. 'SURPLUS_2L' => $data['SURPLUS_2L'],
  1079. 'SURPLUS_3L' => $data['SURPLUS_3L'],
  1080. 'SURPLUS_4L' => $data['SURPLUS_4L'],
  1081. 'SURPLUS_5L' => $data['SURPLUS_5L'],
  1082. 'SURPLUS_LS' => $data['SURPLUS_LS'],
  1083. 'PERIOD_NUM' => $this->_periodNum,
  1084. 'CALC_MONTH' => $this->_calcYearMonth,
  1085. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  1086. 'CREATED_AT' => Date::nowTime(),
  1087. ];
  1088. unset($data);
  1089. return $result;
  1090. }
  1091. /**
  1092. * 本月业绩
  1093. * @param $userId
  1094. * @return array
  1095. */
  1096. public function nowMonthPerfData($userId){
  1097. $data = CalcCache::nowMonthPerf($userId, $this->_periodNum);
  1098. $baseInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  1099. $result = [
  1100. 'ID' => SnowFake::instance()->generateId(),
  1101. 'USER_ID' => $userId,
  1102. 'LAST_DEC_LV' => $baseInfo['DEC_LV'],
  1103. 'LAST_EMP_LV' => $data['EMP_LEVEL'],
  1104. 'LAST_STATUS' => $baseInfo['STATUS'],
  1105. 'FX_AMOUNT_CASH' => $data['FX_AMOUNT_CASH'],
  1106. 'PV_PCS' => $data['PV_PCS'],
  1107. 'PV_PCS_FX' => $data['PV_PCS_FX'],
  1108. 'PV_PSS' => $data['PV_PSS'],
  1109. 'PV_1L' => $data['PV_1L'],
  1110. 'PV_2L' => $data['PV_2L'],
  1111. 'PV_3L' => $data['PV_3L'],
  1112. 'PV_4L' => $data['PV_4L'],
  1113. 'PV_5L' => $data['PV_5L'],
  1114. 'VIP_PV_1L_ZC' => $data['VIP_PV_1L_ZC'],
  1115. 'VIP_PV_2L_ZC' => $data['VIP_PV_2L_ZC'],
  1116. 'VIP_PV_3L_ZC' => $data['VIP_PV_3L_ZC'],
  1117. 'VIP_PV_4L_ZC' => $data['VIP_PV_4L_ZC'],
  1118. 'VIP_PV_5L_ZC' => $data['VIP_PV_5L_ZC'],
  1119. 'PV_1L_TOTAL' => $data['PV_1L_TOTAL'],
  1120. 'PV_2L_TOTAL' => $data['PV_2L_TOTAL'],
  1121. 'PV_3L_TOTAL' => $data['PV_3L_TOTAL'],
  1122. 'PV_4L_TOTAL' => $data['PV_4L_TOTAL'],
  1123. 'PV_5L_TOTAL' => $data['PV_5L_TOTAL'],
  1124. 'PV_PSS_TOTAL' => $data['PV_PSS_TOTAL'],
  1125. 'CF_PERCENT' => $data['CF_PERCENT'],
  1126. 'LX_PERCENT' => $data['LX_PERCENT'],
  1127. 'FX_STATUS' => $data['FX_STATUS'],
  1128. 'CALC_MONTH' => $this->_calcYearMonth,
  1129. 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
  1130. 'CREATED_AT' => Date::nowTime(),
  1131. ];
  1132. unset($data);
  1133. return $result;
  1134. }
  1135. /**
  1136. * 循环父级并执行回调函数
  1137. * @param $userId
  1138. * @param callable $callbackFunc
  1139. * @param int $offset
  1140. * @return bool
  1141. */
  1142. public function loopNetworkParentDo($userId, callable $callbackFunc, int $offset = 0) {
  1143. $allParents = Cache::getAllNetworkParents($userId);
  1144. $allData = array_slice($allParents, $offset, $this->_limit);
  1145. unset($allParents);
  1146. if ($allData) {
  1147. foreach ($allData as $data) {
  1148. $funcResult = $callbackFunc($data);
  1149. if ($funcResult === self::LOOP_FINISH) {
  1150. return true;
  1151. } elseif ($funcResult === self::LOOP_CONTINUE) {
  1152. continue;
  1153. }
  1154. unset($data, $funcResult);
  1155. }
  1156. unset($allData);
  1157. return $this->loopNetworkParentDo($userId, $callbackFunc, $offset + $this->_limit);
  1158. }
  1159. return true;
  1160. }
  1161. /**
  1162. * 循环推荐网络的父级
  1163. * @param $userId
  1164. * @param callable $callbackFunc
  1165. * @param int $offset
  1166. * @return bool
  1167. */
  1168. public function loopRelationParentDo($userId, callable $callbackFunc, int $offset = 0) {
  1169. $allParents = Cache::getAllRelationParents($userId);
  1170. $allData = array_slice($allParents, $offset, $this->_limit);
  1171. unset($allParents);
  1172. if ($allData) {
  1173. foreach ($allData as $data) {
  1174. $funcResult = $callbackFunc($data);
  1175. if ($funcResult === self::LOOP_FINISH) {
  1176. return true;
  1177. } elseif ($funcResult === self::LOOP_CONTINUE) {
  1178. continue;
  1179. }
  1180. unset($data, $funcResult);
  1181. }
  1182. unset($allData);
  1183. return $this->loopRelationParentDo($userId, $callbackFunc, $offset + $this->_limit);
  1184. }
  1185. return true;
  1186. }
  1187. /**
  1188. * ==== 聘级计算开始 ====
  1189. */
  1190. /**
  1191. * 查看会员聘级
  1192. * @param $userId
  1193. * @param $nowMonthPerf
  1194. * @param $userEmpLevelId
  1195. * @return bool|mixed|\yii\db\ActiveRecord
  1196. */
  1197. public function checkEmpLevel($userId, $nowMonthPerf, $userEmpLevelId) {
  1198. $empLevel = $this->_empLevelConfig;
  1199. $userEmpLevel = $empLevel[$userEmpLevelId];
  1200. $userEmpLevelSort = $userEmpLevel['SORT'] ?? EmployLevel::EMP_LEVEL_SORT['NO_LEVEL'];
  1201. $childEmpLevelNumArr = CalcCache::hasEmpLevelNum($userId, $this->_periodNum);
  1202. // 判断主任到首席总监
  1203. $resultLevel = $this->isEmpLevelOther($empLevel, $childEmpLevelNumArr, $userEmpLevelSort);
  1204. unset($empLevel, $childEmpLevelNumArr);
  1205. if( empty($resultLevel) ) {
  1206. //不降级
  1207. if ( $userEmpLevelSort < EmployLevel::EMP_LEVEL_SORT['JX_ZR_LEVEL'] ) {
  1208. //主任
  1209. if ($checkLevel = $this->isEmpLevel1($userId, $nowMonthPerf)) {
  1210. $resultLevel = $checkLevel;
  1211. } else {// 无聘级会员
  1212. $resultLevel = EmployLevel::getLevelFromSort(EmployLevel::EMP_LEVEL_SORT['NO_LEVEL']);
  1213. }
  1214. }else {
  1215. $resultLevel = $userEmpLevel;
  1216. }
  1217. }
  1218. unset($userEmpLevel, $userEmpLevelSort);
  1219. // 获取到级别以后,给上级的相应人数中追加数量
  1220. $parentRecUserId = $userId;
  1221. $this->loopRelationParentDo($userId, function ($parent) use ($resultLevel, &$parentRecUserId) {
  1222. if( !$parentRecUserId ) return self::LOOP_FINISH;
  1223. //判断$parentRecUserId是否为$parent['PARENT_UID']的直推
  1224. $parentUid = $parent['PARENT_UID'];
  1225. $toInfo = CalcCache::getUserInfo($parentRecUserId, $this->_periodNum);
  1226. if( !isset($toInfo['REC_UID']) ) {
  1227. echo $parentRecUserId . PHP_EOL;
  1228. }
  1229. if( isset($toInfo['REC_UID']) && $parentUid !== $toInfo['REC_UID'] ) {
  1230. $parentUid = $toInfo['REC_UID'];
  1231. }
  1232. unset($toInfo);
  1233. if( !$parentUid ) self::LOOP_FINISH;
  1234. CalcCache::hasEmpLevelNum($parentUid, $this->_periodNum, [$parentRecUserId => [$resultLevel['ID'] => 1]]);
  1235. //每次记录上次的USER_ID
  1236. $parentRecUserId = $parentUid;
  1237. });
  1238. return $resultLevel;
  1239. }
  1240. /**
  1241. * 是否达到主任
  1242. * @param $userId
  1243. * @param $nowMonthPerf
  1244. * @return bool|mixed|\yii\db\ActiveRecord
  1245. * @throws \yii\db\Exception
  1246. */
  1247. public function isEmpLevel1($userId, $nowMonthPerf) {
  1248. $level1Option = EmployLevel::getLevelFromSort(EmployLevel::EMP_LEVEL_SORT['JX_ZR_LEVEL']);
  1249. //条件去除最大部门,其它部门累计50万
  1250. if( $nowMonthPerf['PV_PSS_TOTAL'] < $level1Option['OTHER_DEPART_PERF'] ) {//本身业绩就小于50万
  1251. unset($level1Option);
  1252. return false;
  1253. }
  1254. $oneDeepRelation = CalcCache::getChildrenOneDeepFromRedis($userId, $this->_periodNum);
  1255. if ( count($oneDeepRelation) <= 1 ) {//只有一个区就不判断了
  1256. unset($oneDeepRelation, $level1Option);
  1257. return false;
  1258. }
  1259. $maxPvPSS = 0;
  1260. $childPvPssTotalSum = 0;
  1261. foreach ($oneDeepRelation as $childData) {
  1262. //往期业绩
  1263. $userLastPerf = CalcCache::userPerf($childData['USER_ID'], $this->_periodNum);
  1264. //本期业绩
  1265. $periodPerf = CalcCache::nowPeriodPerf($childData['USER_ID'], $this->_periodNum);
  1266. $pvPcsTotal = $userLastPerf['PV_PCS_ZC'] + $userLastPerf['PV_PCS_FX'] + $periodPerf['PV_PCS'];
  1267. $pvPssTotal = $userLastPerf['PV_PSS_TOTAL'] + $periodPerf['PV_PSS'];
  1268. $childPvPssTotal = $pvPcsTotal + $pvPssTotal;
  1269. unset($userLastPerf, $periodPerf, $pvPcsTotal, $pvPssTotal);
  1270. $childPvPssTotalSum += $childPvPssTotal;
  1271. if( $childPvPssTotal >= $maxPvPSS ) {
  1272. $maxPvPSS = $childPvPssTotal;
  1273. }
  1274. unset($childData, $childPvPssTotal);
  1275. }
  1276. unset($oneDeepRelation);
  1277. if( $childPvPssTotalSum - $maxPvPSS >= $level1Option['OTHER_DEPART_PERF'] ) {
  1278. unset($maxPvPSS, $childPvPssTotalSum);
  1279. return $level1Option;
  1280. }else {
  1281. unset($maxPvPSS, $childPvPssTotalSum, $level1Option);
  1282. return false;
  1283. }
  1284. }
  1285. /**
  1286. * 是否达到其他级别
  1287. * @param $empLevel
  1288. * @param $childEmpLevelNumArr
  1289. * @param $userEmpLevelSort
  1290. * @return bool|mixed|\yii\db\ActiveRecord
  1291. */
  1292. public function isEmpLevelOther($empLevel, $childEmpLevelNumArr, $userEmpLevelSort) {
  1293. $resultLevel = [];
  1294. foreach ($empLevel as $level) {
  1295. if ($level['SORT'] < EmployLevel::EMP_LEVEL_SORT['JX_ZR_LEVEL']) continue;
  1296. //不降级
  1297. if ($level['SORT'] <= $userEmpLevelSort) continue;
  1298. // 级别要求有几个区
  1299. $locationNum = $level['LOCATION_NUM'];
  1300. // 级别要求这几个区分别存在级别会员的数量
  1301. $minEmpNum = $level['MIN_EMPLOY_NUM'];
  1302. $minEmpLevel = $level['MIN_EMPLOY_LEVEL'];
  1303. if( !$minEmpLevel || !isset($empLevel[$minEmpLevel]) || $minEmpNum < 1 ) continue;
  1304. $minEmpLevelSort = $empLevel[$minEmpLevel]['SORT'];
  1305. $resultEmpLevelNumArr = array_filter($childEmpLevelNumArr, function ($item, $departUserId) use ($empLevel, $minEmpLevelSort, $minEmpNum) {
  1306. $tempEmpLevelNum = 0;
  1307. foreach ($item as $levelIndex => $levelNum) {
  1308. if ($empLevel[$levelIndex]['SORT'] >= $minEmpLevelSort && $levelNum >= $minEmpNum) {
  1309. $tempEmpLevelNum += 1;
  1310. }
  1311. }
  1312. return $tempEmpLevelNum >= 1;
  1313. }, ARRAY_FILTER_USE_BOTH);
  1314. if (count($resultEmpLevelNumArr) >= $locationNum) {
  1315. if( $level['SORT'] == EmployLevel::EMP_LEVEL_SORT['SHX_ZJ_LEVEL'] ) {
  1316. //首席总监还要满足一个条件、任意部门1个高级总监
  1317. $gjZjLevelNumArr = array_filter($childEmpLevelNumArr, function ($item, $departUserId) use ($empLevel) {
  1318. $tempEmpLevelNum = 0;
  1319. foreach ($item as $levelIndex => $levelNum) {
  1320. if ($empLevel[$levelIndex]['SORT'] >= EmployLevel::EMP_LEVEL_SORT['GJ_ZJ_LEVEL'] && $levelNum >= 1) {
  1321. $tempEmpLevelNum += 1;
  1322. }
  1323. }
  1324. return $tempEmpLevelNum >= 1;
  1325. }, ARRAY_FILTER_USE_BOTH);
  1326. if( count($gjZjLevelNumArr) >= 1 ) {
  1327. $resultLevel = $level;
  1328. }
  1329. unset($gjZjLevelNumArr);
  1330. }else {
  1331. $resultLevel = $level;
  1332. }
  1333. }
  1334. unset($level, $minEmpLevel, $minEmpLevelSort, $resultEmpLevelNumArr);
  1335. }
  1336. unset($empLevel, $childEmpLevelNumArr, $userEmpLevelSort);
  1337. return $resultLevel;
  1338. }
  1339. /**
  1340. * 是否可拿业绩(即注销、关停、停发状态)
  1341. * @param $userId
  1342. * @return bool
  1343. */
  1344. public function isHasPerf($userId) {
  1345. //@todo 所有人都有业绩
  1346. return true;
  1347. }
  1348. /**
  1349. * ==== 聘级计算结束 ====
  1350. */
  1351. /**
  1352. * 更新百分比并发送
  1353. * @param $percent
  1354. */
  1355. private function _updatePercent($percent) {
  1356. // 把数据写入数据库中
  1357. Period::updateAll(['PERF_PERCENT' => $percent], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  1358. \Yii::$app->swooleAsyncTimer->pushAsyncPercentToAdmin($percent, ['MODEL' => 'PERIOD', 'ID' => $this->_periodId, 'FIELD' => 'PERF_PERCENT']);
  1359. }
  1360. }