BonusSend.php 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: leo
  5. * Date: 2018/3/18
  6. * Time: 上午11:06
  7. */
  8. namespace common\helpers\bonus;
  9. use common\components\ActiveRecord;
  10. use common\helpers\DataBak;
  11. use common\helpers\Form;
  12. use common\helpers\LoggerTool;
  13. use common\helpers\Tool;
  14. use common\helpers\user\Balance;
  15. use common\helpers\user\Info;
  16. use common\helpers\user\Reconsume;
  17. use common\helpers\user\Status;
  18. use common\libs\api\sms\SmsApi;
  19. use common\libs\swoole\Process;
  20. use common\models\DealType;
  21. use common\models\FlowRemainPv;
  22. use common\models\PerfPeriod;
  23. use common\models\DecOrder;
  24. use common\models\EmployLevel;
  25. use common\models\StarCrownLevel;
  26. use common\models\UserBonus;
  27. use common\models\UserPerfMonthUpdate;
  28. use common\models\UserPeriodPoints;
  29. use common\models\UserWallet;
  30. use common\models\UserInfo;
  31. use common\models\UserPerf;
  32. use common\models\UserPerfUpdate;
  33. use common\models\UserTeamwork;
  34. use common\models\YearHighestEmpLv;
  35. use common\models\Order;
  36. use common\models\RemainPv;
  37. use common\models\forms\OrderForm;
  38. use yii\base\BaseObject;
  39. use yii\base\StaticInstanceTrait;
  40. use common\helpers\Cache;
  41. use common\helpers\Date;
  42. use common\models\CalcBonus;
  43. use common\models\CalcBonusBD;
  44. use common\models\CalcBonusBS;
  45. use common\models\CalcBonusGX;
  46. use common\models\CalcBonusQY;
  47. use common\models\CalcBonusTG;
  48. use common\models\FlowBonus;
  49. use common\models\PerfActiveUser;
  50. use common\models\PerfMonth;
  51. use common\models\User;
  52. use Yii;
  53. use common\models\Period;
  54. use yii\base\Exception;
  55. use yii\db\Expression;
  56. class BonusSend extends BaseObject {
  57. use StaticInstanceTrait;
  58. private $_limit = 1000;
  59. private $_appUserId = null;
  60. private $_sysConfig = [];
  61. private $_decLevelConfig = [];
  62. private $_empLevelConfig = [];
  63. private $_decRoleConfig = [];
  64. private $_errors = [];
  65. private $_periodNum = 0;
  66. private $_periodId;
  67. private $_isCalcMonth = 0;
  68. private $_isCalcYear = 0;
  69. private $_calcYear;
  70. private $_calcMonth;
  71. private $_calcYearMonth;
  72. private $_lastCalcYear;
  73. private $_lastCalcMonth;
  74. private $_lastCalcYearMonth;
  75. private $_workerId;
  76. private $_workerNum;
  77. public function init(int $periodNum = 0, $appUserId = null, $workerId = null, $workerNum = null) {
  78. parent::init();
  79. $this->_sysConfig = Cache::getSystemConfig();
  80. $this->_decLevelConfig = Cache::getDecLevelConfig();
  81. $this->_empLevelConfig = Cache::getEmpLevelConfig();
  82. $this->_decRoleConfig = CalcCache::getDecRoleConfig($this->_periodNum);
  83. $this->_periodNum = $periodNum;
  84. $this->_appUserId = $appUserId;
  85. $this->_workerId = $workerId;
  86. $this->_workerNum = $workerNum;
  87. }
  88. /**
  89. * 设置期数
  90. * @param int $periodNum
  91. * @return int
  92. */
  93. public function setPeriodNum(int $periodNum) {
  94. return $this->_periodNum = $periodNum;
  95. }
  96. /**
  97. * 获取期数
  98. * @return int
  99. */
  100. public function getPeriodNum() {
  101. return $this->_periodNum;
  102. }
  103. /**
  104. * 加入错误错误
  105. * @param $attr
  106. * @param $error
  107. */
  108. public function addError($attr, $error) {
  109. $this->_errors[$attr][] = $error;
  110. }
  111. /**
  112. * 获取错误信息
  113. * @return array
  114. */
  115. public function getErrors() {
  116. return $this->_errors;
  117. }
  118. /**
  119. * 挂网时处理虚假订单
  120. *
  121. */
  122. public function putFakeOrder() {
  123. echo('开始处理-假订单' . PHP_EOL);
  124. $sysConfig = Cache::getSystemConfig();
  125. $mesureUpCondition = $sysConfig['monthPcsPvFxCondition']['VALUE']; // 月达标条件 NG默认30
  126. $fakeOrder= Order::find()->where(['PERIOD_NUM'=>$this->_periodNum, 'IS_AUTO'=>'1'])->asArray()->all();
  127. foreach($fakeOrder as $fOrder){
  128. $oRemainPv=RemainPv::findOne(["USER_ID"=>$fOrder['USER_ID']]);
  129. $transactionRemain = \Yii::$app->db->beginTransaction();
  130. try{
  131. $fakeOrderPv = $oRemainPv->REMAIN_PV>=$mesureUpCondition?$mesureUpCondition:$oRemainPv->REMAIN_PV;
  132. $flowRemainPvModel = new FlowRemainPv();
  133. $flowRemainPvModel->ID = $this->_generateSn();
  134. $flowRemainPvModel->USER_ID = $fOrder['USER_ID'];
  135. $flowRemainPvModel->REMAIN_PV_FLOW = 0 - $fakeOrderPv;
  136. $flowRemainPvModel->REMAIN_PV_TOTAL = $oRemainPv->REMAIN_PV - $fakeOrderPv;
  137. $flowRemainPvModel->PERIOD_NUM = $this->_periodNum;
  138. $flowRemainPvModel->UPDATED_AT = Date::nowTime();
  139. $flowRemainPvModel->ORDER_SN = $fOrder['SN'];
  140. if(!$flowRemainPvModel->save()){
  141. $this->addErrors($flowRemainPvModel->getErrors());
  142. return false;
  143. }
  144. $oRemainPv->updateCounters(['REMAIN_PV'=>0-$fakeOrderPv]);
  145. $transactionRemain->commit();
  146. } catch (Exception $e) {
  147. $transactionRemain->rollBack();
  148. $this->addError('add', $e->getMessage());
  149. return null;
  150. }
  151. }
  152. echo('假订单处理完' . PHP_EOL);
  153. return true; // $flowRemainPvModel;
  154. }
  155. /**
  156. * 生成流水号
  157. * @return string
  158. */
  159. private function _generateSn() {
  160. return Date::today('Ymd') . $this->_random(10, 1);
  161. }
  162. /**
  163. * 生成随机数
  164. * @param $length
  165. * @param int $numeric
  166. * @return string
  167. */
  168. private function _random($length, $numeric = 0) {
  169. $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
  170. $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed));
  171. $hash = '';
  172. $max = strlen($seed) - 1;
  173. for ($i = 0; $i < $length; $i++) {
  174. $hash .= $seed[mt_rand(0, $max)];
  175. }
  176. return $hash;
  177. }
  178. /**
  179. * 进行奖金发放步骤
  180. * @return bool
  181. */
  182. public function sendStep() {
  183. try {
  184. $t1 = microtime(true);
  185. // 初始化
  186. $this->initTask();
  187. echo('挂网开始');
  188. $this->putFakeOrder();
  189. // 先把有remainPv的订单处理一下,将remainPv加入到remain_pv及流水表
  190. echo('处理当期REMAIN PV ' . date('Y-m-d H:i:s', time()) . PHP_EOL);
  191. $this->_calcRemainPv();
  192. $t2 = microtime(true);
  193. echo('初始化完成,当前期数【' . $this->_periodNum . '】,耗时:' . round($t2 - $t1, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  194. // 改变状态
  195. $this->setSendStatus('start');
  196. $t3 = microtime(true);
  197. echo('改变状态完成,耗时:' . round($t3 - $t2, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  198. $this->_updatePercent(20);
  199. //Yii::$app->db->close();
  200. //Yii::$app->dbShop->close();
  201. // 更新StarDirectory
  202. $this->updateEmpLevel();
  203. $this->_updatePercent(40);
  204. $t4 = microtime(true);
  205. echo('更新聘级完成,耗时:' . round($t4 - $t3, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  206. // 更新StarCrown
  207. $this->updateCrownLevel();
  208. $this->_updatePercent(50);
  209. $t41 = microtime(true);
  210. echo('更新星级完成,耗时:' . round($t41 - $t4, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  211. // 发放奖金
  212. $this->sendBonusLoop();
  213. $this->_updatePercent(60);
  214. $t5 = microtime(true);
  215. echo('发放奖金完成,耗时:' . round($t5 - $t4, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  216. // 更新会员上次报单级别
  217. // $this->updateUserDevLv();
  218. // $this->_updatePercent(80);
  219. $t6 = microtime(true);
  220. // echo('更新会员上次报单级别完成,耗时:' . round($t6 - $t5, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  221. // 更新会员累计业绩
  222. $this->updateUserPerf();
  223. $this->_updatePercent(80);
  224. $t7 = microtime(true);
  225. echo('更新会员累计业绩完成,耗时:' . round($t7 - $t6, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  226. $this->updateUserPerfMonth();
  227. $this->_updatePercent(90);
  228. $t8 = microtime(true);
  229. echo('更新会员累计月业绩完成,耗时:' . round($t8 - $t7, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  230. // 更新活跃用户状态,更新为已处理
  231. // $this->updateActiveUser();
  232. // $this->_updatePercent(95);
  233. // $t9 = microtime(true);
  234. // echo('更新会员累计月业绩完成,耗时:' . round($t9 - $t8, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  235. // 开启子进程去完成下面的循环发放和循环改聘级和循环更新累计业绩
  236. /*$process = new Process('sendBonus', 3);
  237. $process->run(function($workId, $pmid){
  238. $this->processStep($workId);
  239. });
  240. while (true){
  241. if($process->isFinish()) break;
  242. }
  243. unset($process);
  244. $this->_updatePercent(90);
  245. $t4 = microtime(true);
  246. echo('所有子进程的任务完成,耗时:'.round($t4 - $t3, 3).',内存使用:'.(round(memory_get_usage()/1024/1024, 3)).'MB'.PHP_EOL);*/
  247. echo('全部奖金发放完成,耗时:'.round($t7 - $t1, 3).',内存使用:'.(round(memory_get_usage()/1024/1024, 3)).'MB'.PHP_EOL);
  248. $this->_updatePercent(100);
  249. } catch (\Exception $e) {
  250. LoggerTool::error('sendBonus' . sprintf('File【%s】, Line【%s】, Msg【%s】', $e->getFile(), $e->getLine(), $e->getMessage()));
  251. $this->addError('sendBonus', sprintf('%s',$e->getMessage()));
  252. return false;
  253. }
  254. if (count($this->_errors) > 0) {
  255. return false;
  256. }
  257. return true;
  258. }
  259. /**
  260. * 处理order表中有remain_pv的订单
  261. * 将结果写入到remainPv相关表中
  262. *
  263. */
  264. private function _calcRemainPv(){
  265. $sysConfig = Cache::getSystemConfig();
  266. $mesureUpCondition = $sysConfig['monthPcsPvFxCondition']['VALUE']; // 月达标条件 NG默认30
  267. $orders = Order::find()->where('PERIOD_NUM=:PERIOD_NUM AND REMAIN_PV>0 AND IS_DELETE=0',[':PERIOD_NUM'=>$this->_periodNum])->asArray()->all();
  268. foreach($orders as $order){
  269. $oRemainPv = RemainPv::find()->where(['USER_ID' => $order['USER_ID']])->one();
  270. $myRemainPv = $oRemainPv?$oRemainPv['REMAIN_PV']:0;
  271. $transactionRemain = \Yii::$app->db->beginTransaction();
  272. try{
  273. $flowRemainPvModel = new FlowRemainPv();
  274. $flowRemainPvModel->ID = $this->_generateSn();
  275. $flowRemainPvModel->USER_ID = $order['USER_ID'];
  276. $flowRemainPvModel->REMAIN_PV_FLOW = $order['REMAIN_PV'];
  277. $flowRemainPvModel->REMAIN_PV_TOTAL = $myRemainPv + $order['REMAIN_PV'];
  278. $flowRemainPvModel->PERIOD_NUM = $this->_periodNum;
  279. $flowRemainPvModel->UPDATED_AT = Date::nowTime();
  280. $flowRemainPvModel->ORDER_SN = $order['SN'];
  281. if(!$flowRemainPvModel->save()){
  282. $this->addErrors($flowRemainPvModel->getErrors());
  283. return false;
  284. }
  285. $oRemainPv = RemainPv::find()->where(['USER_ID' => $order['USER_ID']])->one();
  286. if($oRemainPv){
  287. $oRemainPv->updateCounters(['REMAIN_PV'=>$order['REMAIN_PV']]);
  288. }else{
  289. $remainPvModel = new RemainPv();
  290. $remainPvModel->ID = $this->_generateSn();
  291. $remainPvModel->USER_ID = $order['USER_ID'];
  292. $remainPvModel->UPDATED_AT = Date::nowTime();
  293. $remainPvModel->REMAIN_PV = $order['REMAIN_PV'];
  294. $remainPvModel->STATUS = 1;
  295. if(!$remainPvModel->save()){
  296. $this->addErrors($remainPvModel->getErrors());
  297. return false;
  298. }
  299. }
  300. $transactionRemain->commit();
  301. } catch (Exception $e) {
  302. $transactionRemain->rollBack();
  303. $this->addError('add', $e->getMessage());
  304. return null;
  305. }
  306. }
  307. return null;
  308. }
  309. /**
  310. * 需要多进程执行的任务
  311. * @param $workId
  312. * @throws \yii\db\Exception
  313. */
  314. public function processStep($workId = null) {
  315. if ($workId == 1 || $workId === null) {
  316. // 发放奖金
  317. $this->sendBonusLoop();
  318. }
  319. if ($workId == 2 || $workId === null) {
  320. // 更新聘级
  321. $this->updateEmpLevel();
  322. }
  323. if ($workId == 3 || $workId === null) {
  324. // 更新会员累计业绩
  325. $this->updateUserPerf();
  326. }
  327. Yii::$app->db->close();
  328. Yii::$app->dbShop->close();
  329. }
  330. /**
  331. * 初始化发放任务
  332. * @throws Exception
  333. */
  334. public function initTask() {
  335. $this->_errors = [];
  336. $periodNum = $this->_periodNum;
  337. // 获取传入期数的相关信息
  338. $periodObj = Period::instance();
  339. $periodDataArr = $periodObj->setPeriodNum($periodNum);
  340. $this->_periodId = $periodDataArr['ID'];
  341. $this->_isCalcMonth = $periodObj->isCalcMonth($periodNum);
  342. $this->_calcYear = $periodObj->getYear($periodNum);
  343. $this->_calcMonth = $periodObj->getMonth($periodNum);
  344. $this->_calcYearMonth = $periodObj->getYearMonth($periodNum);
  345. $lastYearMonthArr = $periodObj->getLastMonth($periodNum);
  346. $this->_lastCalcYear = $lastYearMonthArr['year'];
  347. $this->_lastCalcMonth = $lastYearMonthArr['month'];
  348. $this->_lastCalcYearMonth = $lastYearMonthArr['yearMonth'];
  349. }
  350. /**
  351. * 设置结算状态
  352. * @param $type
  353. * start|end|fail
  354. */
  355. public function setSendStatus($type) {
  356. Yii::$app->db->close();
  357. if ($type == 'start') {
  358. Period::updateAll(['IS_SENDING' => 1, 'SEND_STARTED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  359. } elseif ($type == 'end') {
  360. Period::updateAll(['IS_SENDING' => 0, 'IS_SENT' => Period::SEND_FINISH, 'SENT_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  361. } elseif ($type == 'fail') {
  362. Period::updateAll(['IS_SENDING' => 0, 'IS_SENT' => Period::SEND_FAIL, 'SENT_AT' => 0], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  363. }
  364. }
  365. /**
  366. * 结束计算任务
  367. * @throws \yii\db\Exception
  368. */
  369. public function endTask() {
  370. CalcCache::clearAll($this->_periodNum);
  371. $this->setSendStatus('end');
  372. //@todo 先不备份数据
  373. // DataBak::backup($this->_periodNum);
  374. //发送复销短信
  375. // if ($this->_isCalcMonth && $this->_sysConfig['smsOpen']['VALUE']) {
  376. // $this->sendSmsLoop();
  377. // }
  378. }
  379. /**
  380. * 出现错误
  381. */
  382. public function errorTask() {
  383. $this->setSendStatus('fail');
  384. }
  385. /**
  386. * 发放奖金
  387. * @param int $page
  388. * @return bool
  389. * @throws \yii\db\Exception
  390. */
  391. public function sendBonusLoop($page=1) {
  392. echo sprintf("时间:[%s]数据库发奖,当前page为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()), $page);
  393. $periodNum = $this->_periodNum;
  394. // 从奖金结算表中获取当期未发放的所有数据
  395. // $allData = CalcBonus::findUseDbCalc()
  396. $allData = CalcBonus::find()
  397. ->yearMonth($this->_calcYearMonth)
  398. ->where(
  399. '(IS_SENT=0 OR IS_SENT=2) AND CALC_MONTH=:CALC_MONTH AND PERIOD_NUM=:PERIOD_NUM',
  400. [':CALC_MONTH' => $this->_calcYearMonth, ':PERIOD_NUM' => $periodNum]
  401. )
  402. ->limit($this->_limit)
  403. ->asArray()
  404. ->all();
  405. if ($allData) {
  406. $transaction = Yii::$app->db->beginTransaction();
  407. try {
  408. foreach ($allData as $key => $data) {
  409. // 期奖金
  410. // $periodAmount = $data['BONUS_QY'] + $data['BONUS_YC'] + $data['BONUS_XF'] + $data['BONUS_BD'] + $data['BONUS_TG'] + $data['BONUS_YJ'] + $data['BONUS_GX'] + $data['BONUS_GL'];
  411. $periodAmount = $data['BONUS_REAL'];
  412. // 获取本期结算的管理员
  413. // $period = Period::findOneAsArray(['PERIOD_NUM'=>$periodNum]);
  414. // $updateAminId = $period['CALC_ADMIN_ID'];
  415. if ($periodAmount > 0) {
  416. Balance::changeUserBonus($data['USER_ID'], 'bonus', $periodAmount, [
  417. 'CALC_ID' => $data['ID'],
  418. 'REMARK' => 'From Period ' . $periodNum,
  419. 'PERIOD_NUM' => $periodNum,
  420. 'QY' => $data['BONUS_QY'],
  421. 'FW' => $data['BONUS_FW'],
  422. 'YC' => $data['BONUS_YC'],
  423. 'VIP' => $data['BONUS_VIP'],
  424. 'BD' => $data['BONUS_BD'],
  425. 'TG' => $data['BONUS_TG'],
  426. 'YJ' => $data['BONUS_YJ'],
  427. 'GX' => $data['BONUS_GX'],
  428. 'GL' => $data['BONUS_GL'],
  429. 'BS' => $data['BONUS_BS'],
  430. 'BS_MNT' => $data['BONUS_BS_MNT'],
  431. 'BS_ABBR' => $data['BONUS_BS_ABBR'],
  432. 'ORI_QY' => $data['ORI_BONUS_QY'],
  433. 'ORI_YC' => $data['ORI_BONUS_YC'],
  434. 'ORI_VIP' => $data['ORI_BONUS_VIP'],
  435. 'ORI_STANDARD' => $data['ORI_BONUS_STANDARD'],
  436. 'ORI_BD' => $data['ORI_BONUS_BD'],
  437. 'ORI_TG' => $data['ORI_BONUS_TG'],
  438. 'ORI_YJ' => $data['ORI_BONUS_YJ'],
  439. 'ORI_GX' => $data['ORI_BONUS_GX'],
  440. 'ORI_GL' => $data['ORI_BONUS_GL'],
  441. 'ORI_BS' => $data['ORI_BONUS_BS'],
  442. 'ORI_BS_MNT' => $data['ORI_BONUS_BS_MNT'],
  443. 'ORI_BS_ABBR' => $data['ORI_BONUS_BS_ABBR'],
  444. 'RECONSUME_POINTS_TOTAL' => $data['RECONSUME_POINTS'],
  445. 'EXCHANGE_POINTS_TOTAL' => $data['EXCHANGE_POINTS'],
  446. 'MANAGE_TAX' => $data['MANAGE_TAX'],
  447. 'BONUS_TOTAL' => $data['BONUS_TOTAL'],
  448. 'DEAL_TYPE_ID' => DealType::BONUS_SEND,
  449. 'SORT' => $key * 10,
  450. 'BONUS_ISSUE' => true,
  451. ]);
  452. // $this->_teamworkBonus($data['USER_ID'], $periodAmount, $key);
  453. }
  454. // 旅游奖
  455. if ($data['BONUS_TOURISM'] > 0) {
  456. Balance::changeUserBonus($data['USER_ID'], 'tourism_points', $data['BONUS_TOURISM'], [
  457. 'CALC_ID' => $data['ID'],
  458. 'REMARK' => 'From Period ' . $periodNum,
  459. 'PERIOD_NUM' => $periodNum,
  460. 'TOURISM_POINTS' => $data['BONUS_TOURISM'],
  461. 'DEAL_TYPE_ID' => DealType::TOURISM_SEND,
  462. 'SORT' => $key * 10,
  463. 'BONUS_ISSUE' => true,
  464. ]);
  465. }
  466. // 车奖
  467. if ($data['BONUS_GARAGE'] > 0) {
  468. Balance::changeUserBonus($data['USER_ID'], 'garage_points', $data['BONUS_GARAGE'], [
  469. 'CALC_ID' => $data['ID'],
  470. 'REMARK' => 'From Period ' . $periodNum,
  471. 'PERIOD_NUM' => $periodNum,
  472. 'GARAGE_POINTS' => $data['BONUS_GARAGE'],
  473. 'DEAL_TYPE_ID' => DealType::GARAGE_SEND,
  474. 'SORT' => $key * 10,
  475. 'BONUS_ISSUE' => true,
  476. ]);
  477. }
  478. // 房奖
  479. if ($data['BONUS_VILLA'] > 0) {
  480. Balance::changeUserBonus($data['USER_ID'], 'villa_points', $data['BONUS_VILLA'], [
  481. 'CALC_ID' => $data['ID'],
  482. 'REMARK' => 'From Period ' . $periodNum,
  483. 'PERIOD_NUM' => $periodNum,
  484. 'VILLA_POINTS' => $data['BONUS_VILLA'],
  485. 'DEAL_TYPE_ID' => DealType::VILLA_SEND,
  486. 'SORT' => $key * 10,
  487. 'BONUS_ISSUE' => true,
  488. ]);
  489. }
  490. //发放重消积分
  491. // if ($data['RECONSUME_POINTS'] > 0) {
  492. // Balance::changeUserBonus($data['USER_ID'], 'reconsume_points', $data['RECONSUME_POINTS'], [
  493. // 'CALC_ID' => $data['ID'],
  494. // 'REMARK' => 'From ' . $periodNum . '期',
  495. // 'PERIOD_NUM' => $periodNum,
  496. // 'RECONSUME_POINTS' => $data['RECONSUME_POINTS'],
  497. // 'DEAL_TYPE_ID' => DealType::RECONSUME_POINTS_SEND,
  498. // ]);
  499. // }
  500. //
  501. // //发放兑换积分
  502. // if ($data['EXCHANGE_POINTS'] > 0) {
  503. // Balance::changeUserBonus($data['USER_ID'], 'exchange_points', $data['EXCHANGE_POINTS'], [
  504. // 'CALC_ID' => $data['ID'],
  505. // 'REMARK' => 'From ' . $periodNum . '期',
  506. // 'PERIOD_NUM' => $periodNum,
  507. // 'EXCHANGE_POINTS' => $data['EXCHANGE_POINTS'],
  508. // 'DEAL_TYPE_ID' => DealType::EXCHANGE_POINTS_SEND,
  509. // 'BONUS_ISSUE' => true,
  510. // ]);
  511. // }
  512. // 把记录标记为已发放状态
  513. CalcBonus::updateAll(['IS_SENT' => 1, 'SENT_AT' => Date::nowTime()], 'ID=:ID', [':ID' => $data['ID']]);
  514. unset($periodAmount, $key, $data);
  515. }
  516. $transaction->commit();
  517. } catch (Exception $e) {
  518. $transaction->rollBack();
  519. $this->addError('sendBonus', '奖金发放失败,原因:' . $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage());
  520. return false;
  521. }
  522. unset($allData, $transaction);
  523. $page++;
  524. return $this->sendBonusLoop($page);
  525. }
  526. unset($allData);
  527. return true;
  528. }
  529. /**
  530. * 蓝星奖金(即新的管理奖),更新会员聘级
  531. * @param int $offset
  532. * @return bool
  533. * @throws \yii\db\Exception
  534. */
  535. public function updateEmpLevel(int $offset = 0) {
  536. if ($this->_isCalcMonth) {
  537. $empLv = EmployLevel::getIdConvertLevelSortCache();
  538. $allData = CalcBonusBS::find()
  539. ->yearMonth($this->_calcYearMonth)
  540. ->where(
  541. 'CALC_MONTH=:CALC_MONTH AND PERIOD_NUM=:PERIOD_NUM',
  542. [
  543. ':CALC_MONTH' => $this->_calcYearMonth,
  544. ':PERIOD_NUM' =>$this->_periodNum
  545. ]
  546. )
  547. ->orderBy('CREATED_AT DESC')
  548. ->offset($offset)
  549. ->limit($this->_limit)
  550. ->all();
  551. LoggerTool::info($allData);
  552. //@todo 用户级别不变则不更新
  553. $defaultEmpLv = EmployLevel::getDefaultLevelId();
  554. if ($allData) {
  555. $transaction = Yii::$app->db->beginTransaction();
  556. try {
  557. foreach ($allData as $data) {
  558. // 蓝星奖计算的最新聘级
  559. $latestEmpLv = $data['LEVEL_ID']; // 本期计算最新管理级别
  560. $latestEmpLvSort = $empLv[$latestEmpLv]; // 当前蓝星计算的聘级 级别值
  561. if ($defaultEmpLv == $latestEmpLv) {
  562. continue;
  563. }
  564. // 用户存储的最高聘级
  565. $user = CalcCache::getUserInfo($data['USER_ID'], $this->_periodNum);
  566. $highestEmpLv = $user['EMP_LV']; // 用户的历史最高聘级
  567. $highestEmpLvSort = $empLv[$highestEmpLv]; // 历史最高聘级的 级别值
  568. // 如果当前期的聘级高于用户表的最高聘级,则进行更新
  569. if ($latestEmpLvSort > $highestEmpLvSort) {
  570. User::updateAll(['EMP_LV' => $latestEmpLv], 'ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  571. User::deleteBaseInfoFromRedis($data['USER_ID']);
  572. }
  573. // 更新最新用户表级别
  574. User::updateAll([
  575. 'LAST_EMP_LV' => $latestEmpLv,
  576. 'LAST_EMP_LV_UPDATED_AT' => time(),
  577. 'LAST_EMP_LV_UPDATED_PERIOD' => $this->_periodNum
  578. ], 'ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  579. User::deleteBaseInfoFromRedis($data['USER_ID']);
  580. unset($data);
  581. }
  582. $transaction->commit();
  583. } catch (Exception $e) {
  584. $transaction->rollBack();
  585. $this->addError('updateEmpLevel', '更新聘级失败,原因:' . $e->getMessage());
  586. return false;
  587. }
  588. unset($transaction, $allData, $defaultEmpLv);
  589. return $this->updateEmpLevel($offset + $this->_limit);
  590. }
  591. unset($allData);
  592. // 刷新会员EmpLv为0
  593. User::updateAll([
  594. 'LAST_EMP_LV' => '',
  595. 'LAST_EMP_LV_UPDATED_AT' => time(),
  596. 'LAST_EMP_LV_UPDATED_PERIOD' => $this->_periodNum,
  597. ], 'LAST_EMP_LV_UPDATED_PERIOD < :PERIOD_NUM AND LAST_EMP_LV <> ""', [':PERIOD_NUM' => $this->_periodNum]);
  598. User::deleteAllBaseInfoFromRedis();
  599. }
  600. return true;
  601. }
  602. /**
  603. * 更新用户星级
  604. * @param int $offset
  605. * @return bool
  606. */
  607. public function updateCrownLevel(int $offset = 0)
  608. {
  609. $starCrownLv = StarCrownLevel::getIdConvertLevelSortCache();
  610. // $allData = CalcBonusQY::findUseDbCalc()
  611. $allData = CalcBonusQY::find()
  612. ->yearMonth($this->_calcYearMonth)
  613. ->where(
  614. 'CALC_MONTH=:CALC_MONTH AND PERIOD_NUM=:PERIOD_NUM',
  615. [
  616. ':CALC_MONTH' => $this->_calcYearMonth,
  617. ':PERIOD_NUM' =>$this->_periodNum
  618. ]
  619. )
  620. ->orderBy('CREATED_AT DESC')
  621. ->groupBy('USER_ID')
  622. ->offset($offset)
  623. ->limit($this->_limit)
  624. ->all();
  625. $defaultCrownLv = StarCrownLevel::getDefaultLevelId();
  626. if ($allData) {
  627. $transaction = Yii::$app->db->beginTransaction();
  628. try {
  629. foreach ($allData as $data) {
  630. // 默认级别不更新
  631. if( $data['LAST_CROWN_LV'] === $defaultCrownLv ) continue;
  632. $modernCrownLv = $data['LAST_CROWN_LV']; // 本期计算出的最新级别
  633. $user = CalcCache::getUserInfo($data['USER_ID'], $this->_periodNum);
  634. $originCrownLv = $user['CROWN_LV']; // 用户的历史最高crown级别
  635. $originCrownLvSort = $starCrownLv[$originCrownLv]; // 历史最高crown级别值
  636. $modernCrownLvSort = $starCrownLv[$modernCrownLv]; // 当前计算的crown级别值
  637. if ($modernCrownLvSort > $originCrownLvSort) {
  638. $columns = [
  639. 'CROWN_LV' => $data['LAST_CROWN_LV'],
  640. 'LAST_CROWN_LV' => $data['LAST_CROWN_LV'],
  641. 'LAST_CROWN_LV_UPDATED_AT' => time(),
  642. 'LAST_CROWN_LV_UPDATED_PERIOD' => $this->_periodNum,
  643. ];
  644. } else {
  645. $columns = [
  646. 'LAST_CROWN_LV' => $data['LAST_CROWN_LV'],
  647. 'LAST_CROWN_LV_UPDATED_AT' => time(),
  648. 'LAST_CROWN_LV_UPDATED_PERIOD' => $this->_periodNum,
  649. ];
  650. }
  651. User::updateAll($columns, 'ID = :USER_ID', [':USER_ID' => $data['USER_ID']]);
  652. User::deleteBaseInfoFromRedis($data['USER_ID']);
  653. unset($data);
  654. }
  655. $transaction->commit();
  656. } catch (Exception $e) {
  657. $transaction->rollBack();
  658. $this->addError('updateStarCrownLevel', '更新StartCrown失败,原因:' . $e->getMessage());
  659. return false;
  660. }
  661. unset($transaction, $allData);
  662. return $this->updateCrownLevel($offset + $this->_limit);
  663. }
  664. // 刷新会员CrownLv为0
  665. User::updateAll([
  666. 'LAST_CROWN_LV' => '',
  667. 'LAST_CROWN_LV_UPDATED_AT' => time(),
  668. 'LAST_CROWN_LV_UPDATED_PERIOD' => $this->_periodNum,
  669. ], 'LAST_CROWN_LV_UPDATED_PERIOD < :PERIOD_NUM AND LAST_CROWN_LV <> ""' , [':PERIOD_NUM' => $this->_periodNum]);
  670. User::deleteAllBaseInfoFromRedis();
  671. return true;
  672. }
  673. // /**
  674. // * 更新会员聘级
  675. // * @param int $offset
  676. // * @return bool
  677. // * @throws \yii\db\Exception
  678. // */
  679. // public function updateEmpLevel(int $offset = 0) {
  680. // if ($this->_isCalcMonth) {
  681. // $allData = PerfMonth::findUseDbCalc()->yearMonth($this->_calcYearMonth)->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])->offset($offset)->limit($this->_limit)->all();
  682. // //@todo 用户级别不变则不更新
  683. // $defaultEmpLv = EmployLevel::getDefaultLevelId();
  684. // if ($allData) {
  685. // $transaction = Yii::$app->db->beginTransaction();
  686. // try {
  687. // foreach ($allData as $data) {
  688. // //@todo 用户级别不变则不更新
  689. // if( $data['LAST_EMP_LV'] === $defaultEmpLv ) continue;
  690. // User::updateAll(['EMP_LV' => $data['LAST_EMP_LV']], 'ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  691. // User::deleteBaseInfoFromRedis($data['USER_ID']);
  692. // unset($data);
  693. // }
  694. // $transaction->commit();
  695. // } catch (Exception $e) {
  696. // $transaction->rollBack();
  697. // $this->addError('updateEmpLevel', '更新聘级失败,原因:' . $e->getMessage());
  698. // return false;
  699. // }
  700. // unset($transaction, $allData, $defaultEmpLv);
  701. // return $this->updateEmpLevel($offset + $this->_limit);
  702. // }
  703. // unset($allData);
  704. // }
  705. // return true;
  706. // }
  707. // 更活跃会员,将is_send改成1
  708. public function updateActiveUser() {
  709. try {
  710. $ret = PerfActiveUser::updateAll(
  711. ['IS_SENT' => 1],
  712. 'PERIOD_NUM=:PERIOD_NUM AND IS_SENT=:IS_SENT',
  713. ['IS_SENT'=>0, 'PERIOD_NUM'=>$this->_periodNum]
  714. );
  715. return $ret;
  716. } catch(Exception $e) {
  717. $this->addError('updateActiveUser', '更新活跃会员业绩期处理状态失败,原因:' . $e->getMessage());
  718. return false;
  719. }
  720. }
  721. /**
  722. * 更新会员的累计业绩
  723. * @param int $offset
  724. * @return bool
  725. * @throws \yii\db\Exception
  726. */
  727. public function updateUserPerf(int $offset = 0) {
  728. // $allData = PerfPeriod::findUseDbCalc()
  729. $allData = PerfPeriod::find()
  730. ->yearMonth($this->_calcYearMonth)->where('PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum])->orderBy('ID ASC')->offset($offset)->limit($this->_limit)->all();
  731. if ($allData) {
  732. $transaction = Yii::$app->db->beginTransaction();
  733. try {
  734. foreach ($allData as $data) {
  735. $isUpdate = false;
  736. // if (UserPerf::findUseDbCalc()
  737. if (UserPerf::find()
  738. ->where('USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']])->exists()) {
  739. // 判断本期是否已经更新过业绩
  740. // if (!UserPerfUpdate::findUseDbCalc()
  741. if (!UserPerfUpdate::find()
  742. ->yearMonth($this->_calcYearMonth)->where('USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM', [':USER_ID' => $data['USER_ID'], ':PERIOD_NUM' => $this->_periodNum])->exists()) {
  743. $isUpdate = true;
  744. // 更新业绩
  745. UserPerf::updateAll([
  746. 'PV_PCS_ZC' => new Expression('PV_PCS_ZC+' . $data['PV_PCS_ZC']),
  747. 'PV_PCS_YH' => new Expression('PV_PCS_YH+' . $data['PV_PCS_YH']),
  748. 'PV_PCS_ZG' => new Expression('PV_PCS_ZG+' . $data['PV_PCS_ZG']),
  749. 'PV_PCS_LS' => new Expression('PV_PCS_LS+' . $data['PV_PCS_LS']),
  750. 'PV_PCS_FX' => new Expression('PV_PCS_FX+' . $data['PV_PCS_FX']),
  751. 'PV_PSS' => new Expression('PV_PSS+' . $data['PV_PSS']),
  752. 'PV_PSS_TOTAL' => new Expression('PV_PSS_TOTAL+' . $data['PV_PSS']),
  753. 'PV_1L' => new Expression('PV_1L+' . $data['PV_1L']),
  754. 'PV_2L' => new Expression('PV_2L+' . $data['PV_2L']),
  755. 'PV_3L' => new Expression('PV_3L+' . $data['PV_3L']),
  756. 'PV_4L' => new Expression('PV_4L+' . $data['PV_4L']),
  757. 'PV_5L' => new Expression('PV_5L+' . $data['PV_5L']),
  758. 'SURPLUS_1L' => $data['SURPLUS_1L'],
  759. 'SURPLUS_2L' => $data['SURPLUS_2L'],
  760. 'SURPLUS_3L' => $data['SURPLUS_3L'],
  761. 'SURPLUS_4L' => $data['SURPLUS_4L'],
  762. 'SURPLUS_5L' => $data['SURPLUS_5L'],
  763. 'SURPLUS_1L_ZC' => $data['SURPLUS_1L_ZC'],
  764. 'SURPLUS_2L_ZC' => $data['SURPLUS_2L_ZC'],
  765. 'SURPLUS_3L_ZC' => $data['SURPLUS_3L_ZC'],
  766. 'SURPLUS_4L_ZC' => $data['SURPLUS_4L_ZC'],
  767. 'SURPLUS_5L_ZC' => $data['SURPLUS_5L_ZC'],
  768. 'SURPLUS_1L_FX' => $data['SURPLUS_1L_FX'],
  769. 'SURPLUS_2L_FX' => $data['SURPLUS_2L_FX'],
  770. 'SURPLUS_3L_FX' => $data['SURPLUS_3L_FX'],
  771. 'SURPLUS_4L_FX' => $data['SURPLUS_4L_FX'],
  772. 'SURPLUS_5L_FX' => $data['SURPLUS_5L_FX'],
  773. ], 'USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  774. }
  775. } else {
  776. $isUpdate = true;
  777. UserPerf::insertOne([
  778. 'USER_ID' => $data['USER_ID'],
  779. 'PV_PCS_ZC' => $data['PV_PCS_ZC'],
  780. 'PV_PCS_YH' => $data['PV_PCS_YH'],
  781. 'PV_PCS_ZG' => $data['PV_PCS_ZG'],
  782. 'PV_PCS_LS' => $data['PV_PCS_LS'],
  783. 'PV_PCS_FX' => $data['PV_PCS_FX'],
  784. 'PV_PSS' => $data['PV_PSS'],
  785. 'PV_PSS_TOTAL' => $data['PV_PSS'],
  786. 'PV_1L' => $data['PV_1L'],
  787. 'PV_2L' => $data['PV_2L'],
  788. 'PV_3L' => $data['PV_3L'],
  789. 'PV_4L' => $data['PV_4L'],
  790. 'PV_5L' => $data['PV_5L'],
  791. 'SURPLUS_1L' => $data['SURPLUS_1L'],
  792. 'SURPLUS_2L' => $data['SURPLUS_2L'],
  793. 'SURPLUS_3L' => $data['SURPLUS_3L'],
  794. 'SURPLUS_4L' => $data['SURPLUS_4L'],
  795. 'SURPLUS_5L' => $data['SURPLUS_5L'],
  796. 'SURPLUS_1L_ZC' => $data['SURPLUS_1L_ZC'],
  797. 'SURPLUS_2L_ZC' => $data['SURPLUS_2L_ZC'],
  798. 'SURPLUS_3L_ZC' => $data['SURPLUS_3L_ZC'],
  799. 'SURPLUS_4L_ZC' => $data['SURPLUS_4L_ZC'],
  800. 'SURPLUS_5L_ZC' => $data['SURPLUS_5L_ZC'],
  801. 'SURPLUS_1L_FX' => $data['SURPLUS_1L_FX'],
  802. 'SURPLUS_2L_FX' => $data['SURPLUS_2L_FX'],
  803. 'SURPLUS_3L_FX' => $data['SURPLUS_3L_FX'],
  804. 'SURPLUS_4L_FX' => $data['SURPLUS_4L_FX'],
  805. 'SURPLUS_5L_FX' => $data['SURPLUS_5L_FX'],
  806. 'CREATED_AT' => Date::nowTime(),
  807. ]);
  808. }
  809. if ($isUpdate) {
  810. // 变为已更新
  811. UserPerfUpdate::insertOne(['USER_ID' => $data['USER_ID'], 'PERIOD_NUM' => $this->_periodNum, 'P_CALC_MONTH' => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH), 'CREATED_AT' => Date::nowTime()]);
  812. }
  813. unset($data, $isUpdate);
  814. }
  815. $transaction->commit();
  816. } catch (Exception $e) {
  817. $transaction->rollBack();
  818. $this->addError('updateUserPerf', '更新会员业绩失败,原因:' . $e->getMessage());
  819. return false;
  820. }
  821. unset($allData, $transaction);
  822. return $this->updateUserPerf($offset + $this->_limit);
  823. }
  824. unset($allData);
  825. return true;
  826. }
  827. /**
  828. * 更新会员的月剩余业绩
  829. * @param int $offset
  830. * @return bool
  831. * @throws \yii\db\Exception
  832. */
  833. public function updateUserPerfMonth(int $offset = 0) {
  834. // 月结,如果不是月结点,则直接退出
  835. if (!$this->_isCalcMonth) {
  836. return true;
  837. }
  838. // $allData = PerfMonth::findUseDbCalc()
  839. $allData = PerfMonth::find()
  840. ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])->orderBy('ID ASC')->offset($offset)->limit($this->_limit)->all();
  841. if ($allData) {
  842. $transaction = Yii::$app->db->beginTransaction();
  843. try {
  844. foreach ($allData as $data) {
  845. $isUpdate = false;
  846. // if (UserPerf::findUseDbCalc()
  847. if (UserPerf::find()
  848. ->where('USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']])->exists()) {
  849. // 判断本期是否已经更新过业绩
  850. // if (!UserPerfMonthUpdate::findUseDbCalc()
  851. if (!UserPerfMonthUpdate::find()
  852. ->where('USER_ID=:USER_ID AND CALC_MONTH=:CALC_MONTH', [':USER_ID' => $data['USER_ID'], ':CALC_MONTH' => $this->_calcYearMonth])->exists()) {
  853. $isUpdate = true;
  854. // 更新业绩
  855. UserPerf::updateAll([
  856. 'VIP_SURPLUS_1L_ZC' => $data['VIP_SURPLUS_1L_ZC'],
  857. 'VIP_SURPLUS_2L_ZC' => $data['VIP_SURPLUS_2L_ZC'],
  858. 'VIP_SURPLUS_3L_ZC' => $data['VIP_SURPLUS_3L_ZC'],
  859. 'VIP_SURPLUS_4L_ZC' => $data['VIP_SURPLUS_4L_ZC'],
  860. 'VIP_SURPLUS_5L_ZC' => $data['VIP_SURPLUS_5L_ZC']
  861. ], 'USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  862. }
  863. } else {
  864. $isUpdate = true;
  865. UserPerf::insertOne([
  866. 'USER_ID' => $data['USER_ID'],
  867. 'VIP_SURPLUS_1L_ZC' => $data['VIP_SURPLUS_1L_ZC'],
  868. 'VIP_SURPLUS_2L_ZC' => $data['VIP_SURPLUS_2L_ZC'],
  869. 'VIP_SURPLUS_3L_ZC' => $data['VIP_SURPLUS_3L_ZC'],
  870. 'VIP_SURPLUS_4L_ZC' => $data['VIP_SURPLUS_4L_ZC'],
  871. 'VIP_SURPLUS_5L_ZC' => $data['VIP_SURPLUS_5L_ZC'],
  872. 'CREATED_AT' => Date::nowTime(),
  873. ]);
  874. }
  875. if ($isUpdate) {
  876. // 变为已更新
  877. UserPerfMonthUpdate::insertOne(['USER_ID' => $data['USER_ID'], 'CALC_MONTH' => $this->_calcYearMonth, 'CREATED_AT' => Date::nowTime()]);
  878. }
  879. unset($data, $isUpdate);
  880. }
  881. $transaction->commit();
  882. } catch (Exception $e) {
  883. $transaction->rollBack();
  884. $this->addError('updateUserPerf', '更新会员月业绩失败,原因:' . $e->getMessage());
  885. return false;
  886. }
  887. unset($allData, $transaction);
  888. return $this->updateUserPerfMonth($offset + $this->_limit);
  889. }
  890. unset($allData);
  891. return true;
  892. }
  893. /**
  894. * 检测复消积分是否过期
  895. * @return bool
  896. * @throws \yii\db\Exception
  897. */
  898. public function checkReconsumePointsExpired(int $offset = 0) {
  899. $periodNum = $this->_periodNum;
  900. //一期为7天,那么365天为52.14即53期
  901. $expiredPeriodNum = $periodNum - 53;
  902. if( $expiredPeriodNum <= 0 ) return true;
  903. //查询需要过期的期数
  904. $allData = UserPeriodPoints::find()->select('ID,USER_ID,REMAINDER_POINTS')->where('PERIOD_NUM<=:PERIOD_NUM AND EXPIRED=0', ['PERIOD_NUM'=>$expiredPeriodNum])->orderBy('ID ASC')->offset($offset)->limit($this->_limit)->asArray()->all();
  905. if( $allData ) {
  906. $transaction = Yii::$app->db->beginTransaction();
  907. try{
  908. //扣除钱包的复消积分
  909. foreach ($allData as $everyData) {
  910. //过期
  911. UserPeriodPoints::updateAll([
  912. 'REMAINDER_POINTS' => 0,
  913. 'EXPIRED' => 1,
  914. 'EXPIRED_PERIOD' => $periodNum,
  915. 'EXPIRED_AT' => Date::nowTime(),
  916. ], 'ID=:ID', ['ID'=>$everyData['ID']]);
  917. if( !isset($everyData['REMAINDER_POINTS']) || !$everyData['REMAINDER_POINTS'] ) continue;
  918. UserBonus::updateAllCounters([
  919. 'RECONSUME_POINTS' => (-1) * $everyData['REMAINDER_POINTS'],
  920. ], 'USER_ID=:USER_ID', $everyData['USER_ID']);
  921. unset($everyData);
  922. }
  923. unset($periodNum, $expiredPeriodNum, $allData);
  924. $transaction->commit();
  925. }catch (\Exception $e){
  926. $transaction->rollBack();
  927. $this->addError('checkReconsumePointsExpired', '检测过期在复消积分失败,原因:' . $e->getMessage());
  928. return false;
  929. }
  930. return $this->checkReconsumePointsExpired($offset + $this->_limit);
  931. }
  932. unset($allData);
  933. return true;
  934. }
  935. /**
  936. * 更新会员上次报单级别
  937. * @return bool
  938. */
  939. public function updateUserDevLv() {
  940. $transaction = Yii::$app->dbShop->beginTransaction();
  941. try {
  942. \Yii::$app->dbShop->createCommand()->update(User::tableName(), ['LAST_DEC_LV' => new Expression('DEC_LV'), 'LAST_DEC_LV_UPDATED_AT' => Date::nowTime(), 'LAST_DEC_LV_UPDATED_PERIOD' => $this->_periodNum], 'LAST_DEC_LV!=DEC_LV')->execute();
  943. $transaction->commit();
  944. } catch (Exception $e) {
  945. $transaction->rollBack();
  946. $this->addError('updateUserDevLv', '更新会员上次报单级别失败,原因:' . $e->getMessage());
  947. return false;
  948. }
  949. return true;
  950. }
  951. /**
  952. * 给商城会员增加货款
  953. * @param $userId
  954. * @param $amount
  955. * @param $remark
  956. * @throws Exception
  957. */
  958. private function _shopAddPaymentForGoods($userId, $amount, $remark) {
  959. $db = \Yii::$app->dbShop;
  960. $transaction = $db->beginTransaction();
  961. try {
  962. ActiveRecord::batchUpdate(['PAYMENT_FOR_GOODS' => new Expression('PAYMENT_FOR_GOODS+' . abs($amount))], 'USER_ID=:USER_ID', [':USER_ID' => $userId], '{{%USER_WALLET}}', 'dbShop');
  963. // 增加流水
  964. $flowInsertData[] = [
  965. 'USER_ID' => $userId,
  966. 'USER_NAME' => Cache::getUserBaseInfo($userId)['USER_NAME'],
  967. 'DEC_LV' => Cache::getUserBaseInfo($userId)['DEC_LV'],
  968. 'ORDER_SN' => null,
  969. 'AMOUNT' => $amount,
  970. 'CREATED_AT' => Date::nowTime(),
  971. 'PERIOD_AT' => $this->_periodNum,
  972. 'IS_INCR' => 1,
  973. 'REMARK' => $remark,
  974. 'PARTITION_DATE' => Date::ociToDate(),
  975. 'WALLET_TYPE' => 'payment_for_goods',
  976. 'FROM_TYPE' => 'incr',
  977. ];
  978. ActiveRecord::batchInsert($flowInsertData, '{{%FLOW_WALLET}}', 'dbShop');
  979. $transaction->commit();
  980. } catch (Exception $e) {
  981. $transaction->rollBack();
  982. throw new Exception($e->getMessage());
  983. }
  984. }
  985. /**
  986. * 更新百分比并发送
  987. * @param $percent
  988. */
  989. private function _updatePercent($percent) {
  990. // 把数据写入数据库中
  991. Period::updateAll(['SENT_PERCENT' => $percent], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  992. \Yii::$app->swooleAsyncTimer->pushAsyncPercentToAdmin($percent, ['MODEL' => 'PERIOD', 'ID' => $this->_periodId, 'FIELD' => 'SENT_PERCENT']);
  993. }
  994. /**
  995. * 更新最高聘级
  996. * @param $user_id
  997. * @return bool
  998. * @throws Exception
  999. */
  1000. private function _updateHighestEmpLv($user_id) {
  1001. $empLv = Info::getEmpLv($user_id);
  1002. $highEmpLv = Info::getHighEmpLv($user_id);
  1003. $empLvSort = EmployLevel::getSortById($empLv);
  1004. $empLvHighSort = EmployLevel::getSortById($highEmpLv);
  1005. if ($empLvHighYear = YearHighestEmpLv::findOneAsArray('USER_ID=:USER_ID AND YEAR=:YEAR', [':USER_ID' => $user_id, ':YEAR' => $this->_calcYear], 'HIGHEST_EMP_LV_SORT')) {
  1006. if ($empLvSort > $empLvHighYear['HIGHEST_EMP_LV_SORT']) {
  1007. YearHighestEmpLv::updateAll(['HIGHEST_EMP_LV' => $empLv, 'HIGHEST_EMP_LV_SORT' => $empLvSort, 'HIGHEST_EMP_LV_PERIOD' => $this->_periodNum, 'UPDATED_AT' => Date::nowTime()], 'USER_ID=:USER_ID AND YEAR=:YEAR', [':USER_ID' => $user_id, ':YEAR' => $this->_calcYear]);
  1008. }
  1009. } else {
  1010. $yearHighest = new YearHighestEmpLv();
  1011. $yearHighest->USER_ID = $user_id;
  1012. $yearHighest->YEAR = $this->_calcYear;
  1013. $yearHighest->HIGHEST_EMP_LV = $empLv;
  1014. $yearHighest->HIGHEST_EMP_LV_SORT = $empLvSort;
  1015. $yearHighest->HIGHEST_EMP_LV_PERIOD = $this->_periodNum;
  1016. $yearHighest->CREATED_AT = Date::nowTime();
  1017. if (!$yearHighest->save()) {
  1018. throw new \yii\db\Exception(Form::formatErrorsForApi($yearHighest->getErrors()));
  1019. }
  1020. }
  1021. if ($empLvSort > $empLvHighSort) {
  1022. UserInfo::updateAll(['HIGHEST_EMP_LV' => $empLv, 'HIGHEST_EMP_LV_PERIOD' => $this->_periodNum], 'USER_ID=:USER_ID', [':USER_ID' => $user_id]);
  1023. //发送历史最高聘级短信
  1024. if($this->_sysConfig['smsEmpOpen']['VALUE']){
  1025. if(in_array($empLvSort,explode(",",$this->_sysConfig['smsEmp']['VALUE']))){
  1026. $realName = Info::getUserRealNameByUserId($user_id);
  1027. $empLvDate = Date::convert();
  1028. $empLvName = EmployLevel::getNameById($empLv);
  1029. $content = str_replace(['{%REAL_NAME%}', '{%EMP_LV_DATE%}', '{%EMP_LV%}'], [$realName, $empLvDate, $empLvName], $this->_sysConfig['smsContent']['VALUE']);
  1030. $params = [
  1031. 'mobiles' => Info::getUserMobileByUserId($user_id),
  1032. 'content' => $content,
  1033. ];
  1034. SmsApi::instance()->clearError();
  1035. SmsApi::instance()->goSend($params);
  1036. }
  1037. }
  1038. return true;
  1039. }
  1040. return false;
  1041. }
  1042. /**
  1043. * 点位合作奖金
  1044. * @param $userId
  1045. * @param $amount
  1046. * @param null $type
  1047. * @return bool
  1048. * @throws Exception
  1049. * @throws \yii\db\Exception
  1050. */
  1051. private function _teamworkBonus($userId, $amount, $key) {
  1052. if (!$teamwork = UserTeamwork::findAllAsArray('USER_ID!=:MAIN_UID AND MAIN_UID=:MAIN_UID AND IS_DEL=0', [':MAIN_UID' => $userId], 'USER_ID,DIVIDE_PERCENT')) return false;
  1053. $fromUserInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  1054. foreach ($teamwork as $value) {
  1055. $bonus = Tool::formatPrice($amount * $value['DIVIDE_PERCENT'] * 0.01);
  1056. if ($bonus <= 0) continue;
  1057. $toUserInfo = CalcCache::getUserInfo($value['USER_ID'], $this->_periodNum);
  1058. Balance::changeUserBonus($userId, 'bonus', -abs($bonus), ['SORT' => $key*10+1, 'DEAL_TYPE_ID' => DealType::TEAMWORK_TRANSFER_OUT, 'REMARK' => 'To:' . $toUserInfo['USER_NAME'] . ' Per:' . $value['DIVIDE_PERCENT'] . '%']);
  1059. Balance::changeUserBonus($value['USER_ID'], 'bonus', abs($bonus), ['SORT' => $key*10+2, 'DEAL_TYPE_ID' => DealType::TEAMWORK_TRANSFER_IN, 'REMARK' => 'From: ' . $fromUserInfo['USER_NAME'] . ' Per:' . $value['DIVIDE_PERCENT'] . '%']);
  1060. }
  1061. }
  1062. /**
  1063. * 发送短信
  1064. * @param int $offset
  1065. * @return bool
  1066. * @throws \yii\db\Exception
  1067. */
  1068. public function sendSmsLoop(int $offset = 0) {
  1069. // $allData = UserInfo::findUseDbCalc()
  1070. $allData = UserInfo::find()
  1071. ->select('USER_ID,ALLOW_RECONSUME_SMS_TO')->where('ALLOW_RECONSUME_SMS=1')->offset($offset)->limit($this->_limit)->all();
  1072. if ($allData) {
  1073. $smsWallet = explode(",", $this->_sysConfig['smsWallet']['VALUE']);
  1074. $smsFee = $this->_sysConfig['smsFee']['VALUE'];
  1075. //获取剩余月份
  1076. $period = Period::instance();
  1077. $year = $period->getYear($this->_periodNum);
  1078. $monthLeft = Period::getMonthLeft($this->_periodNum);
  1079. $smsFee = Tool::formatPrice($smsFee * $monthLeft);
  1080. $to = Date::yearEnd();
  1081. $transaction = Yii::$app->db->beginTransaction();
  1082. try {
  1083. foreach ($allData as $data) {
  1084. $userId = $data['USER_ID'];
  1085. //过期续费
  1086. if ($data['ALLOW_RECONSUME_SMS_TO'] < Date::nowTime()) {
  1087. foreach ($smsWallet as $item) {
  1088. //1奖金钱包2现金钱包
  1089. if ($item == 1) {
  1090. //看余额是否充足
  1091. if (Balance::getAvailableBalance($userId) < $smsFee){
  1092. UserInfo::updateAll(['ALLOW_RECONSUME_SMS' => 0], 'USER_ID=:USER_ID', [':USER_ID' => $userId]);
  1093. continue;
  1094. };
  1095. Balance::changeUserBonus($userId, 'bonus', -abs($smsFee), ['DEAL_TYPE_ID' => DealType::SMS, 'REMARK' => $year . '年复销提醒短信服务费'], false);
  1096. UserInfo::updateAll(['ALLOW_RECONSUME_SMS_TO' => $to], 'USER_ID=:USER_ID', [':USER_ID' => $userId]);
  1097. $data['ALLOW_RECONSUME_SMS_TO'] = $to;
  1098. break;
  1099. } elseif ($item == 2) {
  1100. throw new Exception('不存在此方式');
  1101. break;
  1102. }
  1103. }
  1104. }
  1105. if ($data['ALLOW_RECONSUME_SMS_TO'] > Date::nowTime() && $mobile = Info::getUserMobileByUserId($userId)) {
  1106. $realName = Info::getUserRealNameByUserId($userId);
  1107. $reconsumPool = Reconsume::getUserReconsumePool($userId);
  1108. $lastRechargeDate = $reconsumPool['toRechargeDate'];
  1109. $isPass = $reconsumPool['isPass'] == 1 ? '合格' : '不合格';
  1110. $month = $period->getNowMonth();
  1111. $content = str_replace(['{%REAL_NAME%}', '{%LAST_RECHARGE_DATE%}', '{%MONTH%}', '{%IS_PASS%}'], [$realName, $lastRechargeDate, $month, $isPass], $this->_sysConfig['smsContent']['VALUE']);
  1112. //todo 发短信函数 待测试
  1113. $params = [
  1114. 'mobiles' => $mobile,
  1115. 'content' => $content,
  1116. ];
  1117. SmsApi::instance()->clearError();
  1118. SmsApi::instance()->goSend($params);
  1119. }
  1120. unset($userId,$mobile,$realName,$reconsumPool,$lastRechargeDate,$isPass,$month,$content);
  1121. }
  1122. $transaction->commit();
  1123. } catch (Exception $e) {
  1124. $transaction->rollBack();
  1125. return false;
  1126. }
  1127. unset($data);
  1128. return $this->sendSmsLoop($offset + $this->_limit);
  1129. }
  1130. unset($allData);
  1131. return true;
  1132. }
  1133. }