BonusSend.php 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  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\Tool;
  13. use common\helpers\user\Balance;
  14. use common\helpers\user\Info;
  15. use common\helpers\user\Reconsume;
  16. use common\helpers\user\Status;
  17. use common\libs\api\sms\SmsApi;
  18. use common\libs\swoole\Process;
  19. use common\models\DealType;
  20. use common\models\PerfPeriod;
  21. use common\models\DecOrder;
  22. use common\models\EmployLevel;
  23. use common\models\UserBonus;
  24. use common\models\UserPerfMonthUpdate;
  25. use common\models\UserPeriodPoints;
  26. use common\models\UserWallet;
  27. use common\models\UserInfo;
  28. use common\models\UserPerf;
  29. use common\models\UserPerfUpdate;
  30. use common\models\UserTeamwork;
  31. use common\models\YearHighestEmpLv;
  32. use yii\base\BaseObject;
  33. use yii\base\StaticInstanceTrait;
  34. use common\helpers\Cache;
  35. use common\helpers\Date;
  36. use common\models\CalcBonus;
  37. use common\models\PerfActiveUser;
  38. use common\models\PerfMonth;
  39. use common\models\User;
  40. use Yii;
  41. use common\models\Period;
  42. use yii\base\Exception;
  43. use yii\db\Expression;
  44. class BonusSend extends BaseObject {
  45. use StaticInstanceTrait;
  46. private $_limit = 1000;
  47. private $_appUserId = null;
  48. private $_sysConfig = [];
  49. private $_decLevelConfig = [];
  50. private $_empLevelConfig = [];
  51. private $_decRoleConfig = [];
  52. private $_errors = [];
  53. private $_periodNum = 0;
  54. private $_periodId;
  55. private $_isCalcMonth = 0;
  56. private $_isCalcYear = 0;
  57. private $_calcYear;
  58. private $_calcMonth;
  59. private $_calcYearMonth;
  60. private $_lastCalcYear;
  61. private $_lastCalcMonth;
  62. private $_lastCalcYearMonth;
  63. private $_workerId;
  64. private $_workerNum;
  65. public function init(int $periodNum = 0, $appUserId = null, $workerId = null, $workerNum = null) {
  66. parent::init();
  67. $this->_sysConfig = Cache::getSystemConfig();
  68. $this->_decLevelConfig = Cache::getDecLevelConfig();
  69. $this->_empLevelConfig = Cache::getEmpLevelConfig();
  70. $this->_decRoleConfig = CalcCache::getDecRoleConfig($this->_periodNum);
  71. $this->_periodNum = $periodNum;
  72. $this->_appUserId = $appUserId;
  73. $this->_workerId = $workerId;
  74. $this->_workerNum = $workerNum;
  75. }
  76. /**
  77. * 设置期数
  78. * @param int $periodNum
  79. * @return int
  80. */
  81. public function setPeriodNum(int $periodNum) {
  82. return $this->_periodNum = $periodNum;
  83. }
  84. /**
  85. * 获取期数
  86. * @return int
  87. */
  88. public function getPeriodNum() {
  89. return $this->_periodNum;
  90. }
  91. /**
  92. * 加入错误错误
  93. * @param $attr
  94. * @param $error
  95. */
  96. public function addError($attr, $error) {
  97. $this->_errors[$attr][] = $error;
  98. }
  99. /**
  100. * 获取错误信息
  101. * @return array
  102. */
  103. public function getErrors() {
  104. return $this->_errors;
  105. }
  106. /**
  107. * 进行奖金发放步骤
  108. * @return bool
  109. */
  110. public function sendStep() {
  111. try {
  112. $t1 = microtime(true);
  113. // 初始化
  114. $this->initTask();
  115. $t2 = microtime(true);
  116. echo('初始化完成,当前期数【' . $this->_periodNum . '】,耗时:' . round($t2 - $t1, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  117. // 改变状态
  118. $this->setSendStatus('start');
  119. $t3 = microtime(true);
  120. echo('改变状态完成,耗时:' . round($t3 - $t2, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  121. $this->_updatePercent(20);
  122. //Yii::$app->db->close();
  123. //Yii::$app->dbShop->close();
  124. // 更新聘级
  125. $this->updateEmpLevel();
  126. $this->_updatePercent(40);
  127. $t4 = microtime(true);
  128. echo('更新聘级完成,耗时:' . round($t4 - $t3, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  129. // 发放奖金
  130. $this->sendBonusLoop();
  131. $this->_updatePercent(60);
  132. $t5 = microtime(true);
  133. echo('发放奖金完成,耗时:' . round($t5 - $t4, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  134. // 更新会员上次报单级别
  135. // $this->updateUserDevLv();
  136. // $this->_updatePercent(80);
  137. $t6 = microtime(true);
  138. // echo('更新会员上次报单级别完成,耗时:' . round($t6 - $t5, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  139. // 更新会员累计业绩
  140. $this->updateUserPerf();
  141. $this->_updatePercent(80);
  142. $t7 = microtime(true);
  143. echo('更新会员累计业绩完成,耗时:' . round($t7 - $t6, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  144. $this->updateUserPerfMonth();
  145. $this->_updatePercent(90);
  146. $t8 = microtime(true);
  147. echo('更新会员累计月业绩完成,耗时:' . round($t8 - $t7, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  148. // 更新活跃用户状态,更新为已处理
  149. $this->updateActiveUser();
  150. $this->_updatePercent(95);
  151. $t9 = microtime(true);
  152. echo('更新会员累计月业绩完成,耗时:' . round($t9 - $t8, 3) . ',内存使用:' . (round(memory_get_usage() / 1024 / 1024, 3)) . 'MB' . PHP_EOL);
  153. // 开启子进程去完成下面的循环发放和循环改聘级和循环更新累计业绩
  154. /*$process = new Process('sendBonus', 3);
  155. $process->run(function($workId, $pmid){
  156. $this->processStep($workId);
  157. });
  158. while (true){
  159. if($process->isFinish()) break;
  160. }
  161. unset($process);
  162. $this->_updatePercent(90);
  163. $t4 = microtime(true);
  164. echo('所有子进程的任务完成,耗时:'.round($t4 - $t3, 3).',内存使用:'.(round(memory_get_usage()/1024/1024, 3)).'MB'.PHP_EOL);*/
  165. echo('全部奖金发放完成,耗时:'.round($t7 - $t1, 3).',内存使用:'.(round(memory_get_usage()/1024/1024, 3)).'MB'.PHP_EOL);
  166. $this->_updatePercent(100);
  167. } catch (\Exception $e) {
  168. $this->addError('sendBonus', $e->getMessage());
  169. return false;
  170. }
  171. if (count($this->_errors) > 0) {
  172. return false;
  173. }
  174. return true;
  175. }
  176. /**
  177. * 需要多进程执行的任务
  178. * @param $workId
  179. * @throws \yii\db\Exception
  180. */
  181. public function processStep($workId = null) {
  182. if ($workId == 1 || $workId === null) {
  183. // 发放奖金
  184. $this->sendBonusLoop();
  185. }
  186. if ($workId == 2 || $workId === null) {
  187. // 更新聘级
  188. $this->updateEmpLevel();
  189. }
  190. if ($workId == 3 || $workId === null) {
  191. // 更新会员累计业绩
  192. $this->updateUserPerf();
  193. }
  194. Yii::$app->db->close();
  195. Yii::$app->dbShop->close();
  196. }
  197. /**
  198. * 初始化发放任务
  199. * @throws Exception
  200. */
  201. public function initTask() {
  202. $this->_errors = [];
  203. $periodNum = $this->_periodNum;
  204. // 获取传入期数的相关信息
  205. $periodObj = Period::instance();
  206. $periodDataArr = $periodObj->setPeriodNum($periodNum);
  207. $this->_periodId = $periodDataArr['ID'];
  208. $this->_isCalcMonth = $periodObj->isCalcMonth($periodNum);
  209. $this->_calcYear = $periodObj->getYear($periodNum);
  210. $this->_calcMonth = $periodObj->getMonth($periodNum);
  211. $this->_calcYearMonth = $periodObj->getYearMonth($periodNum);
  212. $lastYearMonthArr = $periodObj->getLastMonth($periodNum);
  213. $this->_lastCalcYear = $lastYearMonthArr['year'];
  214. $this->_lastCalcMonth = $lastYearMonthArr['month'];
  215. $this->_lastCalcYearMonth = $lastYearMonthArr['yearMonth'];
  216. }
  217. /**
  218. * 设置结算状态
  219. * @param $type
  220. * start|end|fail
  221. */
  222. public function setSendStatus($type) {
  223. Yii::$app->db->close();
  224. if ($type == 'start') {
  225. Period::updateAll(['IS_SENDING' => 1, 'SEND_STARTED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  226. } elseif ($type == 'end') {
  227. Period::updateAll(['IS_SENDING' => 0, 'IS_SENT' => Period::SEND_FINISH, 'SENT_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  228. } elseif ($type == 'fail') {
  229. Period::updateAll(['IS_SENDING' => 0, 'IS_SENT' => Period::SEND_FAIL, 'SENT_AT' => 0], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  230. }
  231. }
  232. /**
  233. * 结束计算任务
  234. * @throws \yii\db\Exception
  235. */
  236. public function endTask() {
  237. CalcCache::clearAll($this->_periodNum);
  238. $this->setSendStatus('end');
  239. //@todo 先不备份数据
  240. // DataBak::backup($this->_periodNum);
  241. //发送复销短信
  242. // if ($this->_isCalcMonth && $this->_sysConfig['smsOpen']['VALUE']) {
  243. // $this->sendSmsLoop();
  244. // }
  245. }
  246. /**
  247. * 出现错误
  248. */
  249. public function errorTask() {
  250. $this->setSendStatus('fail');
  251. }
  252. /**
  253. * 发放奖金
  254. * @param int $page
  255. * @return bool
  256. * @throws \yii\db\Exception
  257. */
  258. public function sendBonusLoop($page=1) {
  259. echo sprintf("时间:[%s]数据库发奖,当前page为:【%s】" . PHP_EOL, date('Y-m-d H:i:s', time()), $page);
  260. $periodNum = $this->_periodNum;
  261. // 从奖金结算表中获取当期未发放的所有数据
  262. $allData = CalcBonus::findUseDbCalc()->yearMonth($this->_calcYearMonth)->where('(IS_SENT=0 OR IS_SENT=2) AND CALC_MONTH=:CALC_MONTH AND PERIOD_NUM=:PERIOD_NUM', [':CALC_MONTH' => $this->_calcYearMonth, ':PERIOD_NUM' => $periodNum])->limit($this->_limit)->asArray()->all();
  263. if ($allData) {
  264. $transaction = Yii::$app->db->beginTransaction();
  265. try {
  266. foreach ($allData as $key => $data) {
  267. // 期奖金
  268. // $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'];
  269. $periodAmount = $data['BONUS_REAL'];
  270. // 获取本期结算的管理员
  271. // $period = Period::findOneAsArray(['PERIOD_NUM'=>$periodNum]);
  272. // $updateAminId = $period['CALC_ADMIN_ID'];
  273. if ($periodAmount > 0) {
  274. Balance::changeUserBonus($data['USER_ID'], 'bonus', $periodAmount, [
  275. 'CALC_ID' => $data['ID'],
  276. 'REMARK' => 'From ' . $periodNum . '期',
  277. 'PERIOD_NUM' => $periodNum,
  278. 'QY' => $data['BONUS_QY'],
  279. 'FW' => $data['BONUS_FW'],
  280. 'YC' => $data['BONUS_YC'],
  281. 'VIP' => $data['BONUS_VIP'],
  282. 'BD' => $data['BONUS_BD'],
  283. 'TG' => $data['BONUS_TG'],
  284. 'YJ' => $data['BONUS_YJ'],
  285. 'GX' => $data['BONUS_GX'],
  286. 'GL' => $data['BONUS_GL'],
  287. 'ORI_QY' => $data['ORI_BONUS_QY'],
  288. 'ORI_YC' => $data['ORI_BONUS_YC'],
  289. 'ORI_VIP' => $data['ORI_BONUS_VIP'],
  290. 'ORI_STANDARD' => $data['ORI_BONUS_STANDARD'],
  291. 'ORI_BD' => $data['ORI_BONUS_BD'],
  292. 'ORI_TG' => $data['ORI_BONUS_TG'],
  293. 'ORI_YJ' => $data['ORI_BONUS_YJ'],
  294. 'ORI_GX' => $data['ORI_BONUS_GX'],
  295. 'ORI_GL' => $data['ORI_BONUS_GL'],
  296. 'RECONSUME_POINTS_TOTAL' => $data['RECONSUME_POINTS'],
  297. 'EXCHANGE_POINTS_TOTAL' => $data['EXCHANGE_POINTS'],
  298. 'MANAGE_TAX' => $data['MANAGE_TAX'],
  299. 'BONUS_TOTAL' => $data['BONUS_TOTAL'],
  300. 'DEAL_TYPE_ID' => DealType::BONUS_SEND,
  301. 'SORT' => $key * 10,
  302. ]);
  303. // $this->_teamworkBonus($data['USER_ID'], $periodAmount, $key);
  304. }
  305. //发放重消积分
  306. if ($data['RECONSUME_POINTS'] > 0) {
  307. Balance::changeUserBonus($data['USER_ID'], 'reconsume_points', $data['RECONSUME_POINTS'], [
  308. 'CALC_ID' => $data['ID'],
  309. 'REMARK' => 'From ' . $periodNum . '期',
  310. 'PERIOD_NUM' => $periodNum,
  311. 'RECONSUME_POINTS' => $data['RECONSUME_POINTS'],
  312. 'DEAL_TYPE_ID' => DealType::RECONSUME_POINTS_SEND,
  313. ]);
  314. }
  315. //发放兑换积分
  316. if ($data['EXCHANGE_POINTS'] > 0) {
  317. Balance::changeUserBonus($data['USER_ID'], 'exchange_points', $data['RECONSUME_POINTS'], [
  318. 'CALC_ID' => $data['ID'],
  319. 'REMARK' => 'From ' . $periodNum . '期',
  320. 'PERIOD_NUM' => $periodNum,
  321. 'EXCHANGE_POINTS' => $data['EXCHANGE_POINTS'],
  322. 'DEAL_TYPE_ID' => DealType::EXCHANGE_POINTS_SEND,
  323. ]);
  324. }
  325. // 把记录标记为已发放状态
  326. CalcBonus::updateAll(['IS_SENT' => 1, 'SENT_AT' => Date::nowTime()], 'ID=:ID', [':ID' => $data['ID']]);
  327. unset($periodAmount, $key, $data);
  328. }
  329. $transaction->commit();
  330. } catch (Exception $e) {
  331. $transaction->rollBack();
  332. $this->addError('sendBonus', '奖金发放失败,原因:' . $e->getMessage());
  333. return false;
  334. }
  335. unset($allData, $transaction);
  336. $page++;
  337. return $this->sendBonusLoop($page);
  338. }
  339. unset($allData);
  340. return true;
  341. }
  342. /**
  343. * 更新会员聘级
  344. * @param int $offset
  345. * @return bool
  346. * @throws \yii\db\Exception
  347. */
  348. public function updateEmpLevel(int $offset = 0) {
  349. if ($this->_isCalcMonth) {
  350. $allData = PerfMonth::findUseDbCalc()->yearMonth($this->_calcYearMonth)->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])->offset($offset)->limit($this->_limit)->all();
  351. //@todo 用户级别不变则不更新
  352. $defaultEmpLv = EmployLevel::getDefaultLevelId();
  353. if ($allData) {
  354. $transaction = Yii::$app->db->beginTransaction();
  355. try {
  356. foreach ($allData as $data) {
  357. //@todo 用户级别不变则不更新
  358. if( $data['LAST_EMP_LV'] === $defaultEmpLv ) continue;
  359. User::updateAll(['EMP_LV' => $data['LAST_EMP_LV']], 'ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  360. User::deleteBaseInfoFromRedis($data['USER_ID']);
  361. unset($data);
  362. }
  363. $transaction->commit();
  364. } catch (Exception $e) {
  365. $transaction->rollBack();
  366. $this->addError('updateEmpLevel', '更新聘级失败,原因:' . $e->getMessage());
  367. return false;
  368. }
  369. unset($transaction, $allData, $defaultEmpLv);
  370. return $this->updateEmpLevel($offset + $this->_limit);
  371. }
  372. unset($allData);
  373. }
  374. return true;
  375. }
  376. // 更活跃会员,将is_send改成1
  377. public function updateActiveUser() {
  378. try {
  379. $ret = PerfActiveUser::updateAll(
  380. ['IS_SENT' => 1],
  381. 'PERIOD_NUM=:PERIOD_NUM AND IS_SENT=:IS_SENT',
  382. ['IS_SENT'=>0, 'PERIOD_NUM'=>$this->_periodNum]
  383. );
  384. return $ret;
  385. } catch(Exception $e) {
  386. $this->addError('updateActiveUser', '更新活跃会员业绩期处理状态失败,原因:' . $e->getMessage());
  387. return false;
  388. }
  389. }
  390. /**
  391. * 更新会员的累计业绩
  392. * @param int $offset
  393. * @return bool
  394. * @throws \yii\db\Exception
  395. */
  396. public function updateUserPerf(int $offset = 0) {
  397. $allData = PerfPeriod::findUseDbCalc()->yearMonth($this->_calcYearMonth)->where('PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum])->orderBy('ID ASC')->offset($offset)->limit($this->_limit)->all();
  398. if ($allData) {
  399. $transaction = Yii::$app->db->beginTransaction();
  400. try {
  401. foreach ($allData as $data) {
  402. $isUpdate = false;
  403. if (UserPerf::findUseDbCalc()->where('USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']])->exists()) {
  404. // 判断本期是否已经更新过业绩
  405. if (!UserPerfUpdate::findUseDbCalc()->yearMonth($this->_calcYearMonth)->where('USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM', [':USER_ID' => $data['USER_ID'], ':PERIOD_NUM' => $this->_periodNum])->exists()) {
  406. $isUpdate = true;
  407. // 更新业绩
  408. UserPerf::updateAll([
  409. 'PV_PCS_ZC' => new Expression('PV_PCS_ZC+' . $data['PV_PCS_ZC']),
  410. 'PV_PCS_YH' => new Expression('PV_PCS_YH+' . $data['PV_PCS_YH']),
  411. 'PV_PCS_ZG' => new Expression('PV_PCS_ZG+' . $data['PV_PCS_ZG']),
  412. 'PV_PCS_LS' => new Expression('PV_PCS_LS+' . $data['PV_PCS_LS']),
  413. 'PV_PCS_FX' => new Expression('PV_PCS_FX+' . $data['PV_PCS_FX']),
  414. 'PV_PSS' => new Expression('PV_PSS+' . $data['PV_PSS']),
  415. 'PV_PSS_TOTAL' => new Expression('PV_PSS_TOTAL+' . $data['PV_PSS']),
  416. 'PV_1L' => new Expression('PV_1L+' . $data['PV_1L']),
  417. 'PV_2L' => new Expression('PV_2L+' . $data['PV_2L']),
  418. 'PV_3L' => new Expression('PV_3L+' . $data['PV_3L']),
  419. 'PV_4L' => new Expression('PV_4L+' . $data['PV_4L']),
  420. 'PV_5L' => new Expression('PV_5L+' . $data['PV_5L']),
  421. 'SURPLUS_1L' => $data['SURPLUS_1L'],
  422. 'SURPLUS_2L' => $data['SURPLUS_2L'],
  423. 'SURPLUS_3L' => $data['SURPLUS_3L'],
  424. 'SURPLUS_4L' => $data['SURPLUS_4L'],
  425. 'SURPLUS_5L' => $data['SURPLUS_5L'],
  426. 'SURPLUS_1L_ZC' => $data['SURPLUS_1L_ZC'],
  427. 'SURPLUS_2L_ZC' => $data['SURPLUS_2L_ZC'],
  428. 'SURPLUS_3L_ZC' => $data['SURPLUS_3L_ZC'],
  429. 'SURPLUS_4L_ZC' => $data['SURPLUS_4L_ZC'],
  430. 'SURPLUS_5L_ZC' => $data['SURPLUS_5L_ZC'],
  431. 'SURPLUS_1L_FX' => $data['SURPLUS_1L_FX'],
  432. 'SURPLUS_2L_FX' => $data['SURPLUS_2L_FX'],
  433. 'SURPLUS_3L_FX' => $data['SURPLUS_3L_FX'],
  434. 'SURPLUS_4L_FX' => $data['SURPLUS_4L_FX'],
  435. 'SURPLUS_5L_FX' => $data['SURPLUS_5L_FX'],
  436. ], 'USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  437. }
  438. } else {
  439. $isUpdate = true;
  440. UserPerf::insertOne([
  441. 'USER_ID' => $data['USER_ID'],
  442. 'PV_PCS_ZC' => $data['PV_PCS_ZC'],
  443. 'PV_PCS_YH' => $data['PV_PCS_YH'],
  444. 'PV_PCS_ZG' => $data['PV_PCS_ZG'],
  445. 'PV_PCS_LS' => $data['PV_PCS_LS'],
  446. 'PV_PCS_FX' => $data['PV_PCS_FX'],
  447. 'PV_PSS' => $data['PV_PSS'],
  448. 'PV_PSS_TOTAL' => $data['PV_PSS'],
  449. 'PV_1L' => $data['PV_1L'],
  450. 'PV_2L' => $data['PV_2L'],
  451. 'PV_3L' => $data['PV_3L'],
  452. 'PV_4L' => $data['PV_4L'],
  453. 'PV_5L' => $data['PV_5L'],
  454. 'SURPLUS_1L' => $data['SURPLUS_1L'],
  455. 'SURPLUS_2L' => $data['SURPLUS_2L'],
  456. 'SURPLUS_3L' => $data['SURPLUS_3L'],
  457. 'SURPLUS_4L' => $data['SURPLUS_4L'],
  458. 'SURPLUS_5L' => $data['SURPLUS_5L'],
  459. 'SURPLUS_1L_ZC' => $data['SURPLUS_1L_ZC'],
  460. 'SURPLUS_2L_ZC' => $data['SURPLUS_2L_ZC'],
  461. 'SURPLUS_3L_ZC' => $data['SURPLUS_3L_ZC'],
  462. 'SURPLUS_4L_ZC' => $data['SURPLUS_4L_ZC'],
  463. 'SURPLUS_5L_ZC' => $data['SURPLUS_5L_ZC'],
  464. 'SURPLUS_1L_FX' => $data['SURPLUS_1L_FX'],
  465. 'SURPLUS_2L_FX' => $data['SURPLUS_2L_FX'],
  466. 'SURPLUS_3L_FX' => $data['SURPLUS_3L_FX'],
  467. 'SURPLUS_4L_FX' => $data['SURPLUS_4L_FX'],
  468. 'SURPLUS_5L_FX' => $data['SURPLUS_5L_FX'],
  469. 'CREATED_AT' => Date::nowTime(),
  470. ]);
  471. }
  472. if ($isUpdate) {
  473. // 变为已更新
  474. 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()]);
  475. }
  476. unset($data, $isUpdate);
  477. }
  478. $transaction->commit();
  479. } catch (Exception $e) {
  480. $transaction->rollBack();
  481. $this->addError('updateUserPerf', '更新会员业绩失败,原因:' . $e->getMessage());
  482. return false;
  483. }
  484. unset($allData, $transaction);
  485. return $this->updateUserPerf($offset + $this->_limit);
  486. }
  487. unset($allData);
  488. return true;
  489. }
  490. /**
  491. * 更新会员的月剩余业绩
  492. * @param int $offset
  493. * @return bool
  494. * @throws \yii\db\Exception
  495. */
  496. public function updateUserPerfMonth(int $offset = 0) {
  497. // 月结,如果不是月结点,则直接退出
  498. if (!$this->_isCalcMonth) {
  499. return true;
  500. }
  501. $allData = PerfMonth::findUseDbCalc()->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])->orderBy('ID ASC')->offset($offset)->limit($this->_limit)->all();
  502. if ($allData) {
  503. $transaction = Yii::$app->db->beginTransaction();
  504. try {
  505. foreach ($allData as $data) {
  506. $isUpdate = false;
  507. if (UserPerf::findUseDbCalc()->where('USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']])->exists()) {
  508. // 判断本期是否已经更新过业绩
  509. if (!UserPerfMonthUpdate::findUseDbCalc()->where('USER_ID=:USER_ID AND CALC_MONTH=:CALC_MONTH', [':USER_ID' => $data['USER_ID'], ':CALC_MONTH' => $this->_calcYearMonth])->exists()) {
  510. $isUpdate = true;
  511. // 更新业绩
  512. UserPerf::updateAll([
  513. 'VIP_SURPLUS_1L_ZC' => $data['VIP_SURPLUS_1L_ZC'],
  514. 'VIP_SURPLUS_2L_ZC' => $data['VIP_SURPLUS_2L_ZC'],
  515. 'VIP_SURPLUS_3L_ZC' => $data['VIP_SURPLUS_3L_ZC'],
  516. 'VIP_SURPLUS_4L_ZC' => $data['VIP_SURPLUS_4L_ZC'],
  517. 'VIP_SURPLUS_5L_ZC' => $data['VIP_SURPLUS_5L_ZC']
  518. ], 'USER_ID=:USER_ID', [':USER_ID' => $data['USER_ID']]);
  519. }
  520. } else {
  521. $isUpdate = true;
  522. UserPerf::insertOne([
  523. 'USER_ID' => $data['USER_ID'],
  524. 'VIP_SURPLUS_1L_ZC' => $data['VIP_SURPLUS_1L_ZC'],
  525. 'VIP_SURPLUS_2L_ZC' => $data['VIP_SURPLUS_2L_ZC'],
  526. 'VIP_SURPLUS_3L_ZC' => $data['VIP_SURPLUS_3L_ZC'],
  527. 'VIP_SURPLUS_4L_ZC' => $data['VIP_SURPLUS_4L_ZC'],
  528. 'VIP_SURPLUS_5L_ZC' => $data['VIP_SURPLUS_5L_ZC'],
  529. 'CREATED_AT' => Date::nowTime(),
  530. ]);
  531. }
  532. if ($isUpdate) {
  533. // 变为已更新
  534. UserPerfMonthUpdate::insertOne(['USER_ID' => $data['USER_ID'], 'CALC_MONTH' => $this->_calcYearMonth, 'CREATED_AT' => Date::nowTime()]);
  535. }
  536. unset($data, $isUpdate);
  537. }
  538. $transaction->commit();
  539. } catch (Exception $e) {
  540. $transaction->rollBack();
  541. $this->addError('updateUserPerf', '更新会员月业绩失败,原因:' . $e->getMessage());
  542. return false;
  543. }
  544. unset($allData, $transaction);
  545. return $this->updateUserPerfMonth($offset + $this->_limit);
  546. }
  547. unset($allData);
  548. return true;
  549. }
  550. /**
  551. * 检测复消积分是否过期
  552. * @return bool
  553. * @throws \yii\db\Exception
  554. */
  555. public function checkReconsumePointsExpired(int $offset = 0) {
  556. $periodNum = $this->_periodNum;
  557. //一期为7天,那么365天为52.14即53期
  558. $expiredPeriodNum = $periodNum - 53;
  559. if( $expiredPeriodNum <= 0 ) return true;
  560. //查询需要过期的期数
  561. $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();
  562. if( $allData ) {
  563. $transaction = Yii::$app->db->beginTransaction();
  564. try{
  565. //扣除钱包的复消积分
  566. foreach ($allData as $everyData) {
  567. //过期
  568. UserPeriodPoints::updateAll([
  569. 'REMAINDER_POINTS' => 0,
  570. 'EXPIRED' => 1,
  571. 'EXPIRED_PERIOD' => $periodNum,
  572. 'EXPIRED_AT' => Date::nowTime(),
  573. ], 'ID=:ID', ['ID'=>$everyData['ID']]);
  574. if( !isset($everyData['REMAINDER_POINTS']) || !$everyData['REMAINDER_POINTS'] ) continue;
  575. UserBonus::updateAllCounters([
  576. 'RECONSUME_POINTS' => (-1) * $everyData['REMAINDER_POINTS'],
  577. ], 'USER_ID=:USER_ID', $everyData['USER_ID']);
  578. unset($everyData);
  579. }
  580. unset($periodNum, $expiredPeriodNum, $allData);
  581. $transaction->commit();
  582. }catch (\Exception $e){
  583. $transaction->rollBack();
  584. $this->addError('checkReconsumePointsExpired', '检测过期在复消积分失败,原因:' . $e->getMessage());
  585. return false;
  586. }
  587. return $this->checkReconsumePointsExpired($offset + $this->_limit);
  588. }
  589. unset($allData);
  590. return true;
  591. }
  592. /**
  593. * 更新会员上次报单级别
  594. * @return bool
  595. */
  596. public function updateUserDevLv() {
  597. $transaction = Yii::$app->dbShop->beginTransaction();
  598. try {
  599. \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();
  600. $transaction->commit();
  601. } catch (Exception $e) {
  602. $transaction->rollBack();
  603. $this->addError('updateUserDevLv', '更新会员上次报单级别失败,原因:' . $e->getMessage());
  604. return false;
  605. }
  606. return true;
  607. }
  608. /**
  609. * 给商城会员增加货款
  610. * @param $userId
  611. * @param $amount
  612. * @param $remark
  613. * @throws Exception
  614. */
  615. private function _shopAddPaymentForGoods($userId, $amount, $remark) {
  616. $db = \Yii::$app->dbShop;
  617. $transaction = $db->beginTransaction();
  618. try {
  619. ActiveRecord::batchUpdate(['PAYMENT_FOR_GOODS' => new Expression('PAYMENT_FOR_GOODS+' . abs($amount))], 'USER_ID=:USER_ID', [':USER_ID' => $userId], '{{%USER_WALLET}}', 'dbShop');
  620. // 增加流水
  621. $flowInsertData[] = [
  622. 'USER_ID' => $userId,
  623. 'USER_NAME' => Cache::getUserBaseInfo($userId)['USER_NAME'],
  624. 'DEC_LV' => Cache::getUserBaseInfo($userId)['DEC_LV'],
  625. 'ORDER_SN' => null,
  626. 'AMOUNT' => $amount,
  627. 'CREATED_AT' => Date::nowTime(),
  628. 'PERIOD_AT' => $this->_periodNum,
  629. 'IS_INCR' => 1,
  630. 'REMARK' => $remark,
  631. 'PARTITION_DATE' => Date::ociToDate(),
  632. 'WALLET_TYPE' => 'payment_for_goods',
  633. 'FROM_TYPE' => 'incr',
  634. ];
  635. ActiveRecord::batchInsert($flowInsertData, '{{%FLOW_WALLET}}', 'dbShop');
  636. $transaction->commit();
  637. } catch (Exception $e) {
  638. $transaction->rollBack();
  639. throw new Exception($e->getMessage());
  640. }
  641. }
  642. /**
  643. * 更新百分比并发送
  644. * @param $percent
  645. */
  646. private function _updatePercent($percent) {
  647. // 把数据写入数据库中
  648. Period::updateAll(['SENT_PERCENT' => $percent], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
  649. \Yii::$app->swooleAsyncTimer->pushAsyncPercentToAdmin($percent, ['MODEL' => 'PERIOD', 'ID' => $this->_periodId, 'FIELD' => 'SENT_PERCENT']);
  650. }
  651. /**
  652. * 更新最高聘级
  653. * @param $user_id
  654. * @return bool
  655. * @throws Exception
  656. */
  657. private function _updateHighestEmpLv($user_id) {
  658. $empLv = Info::getEmpLv($user_id);
  659. $highEmpLv = Info::getHighEmpLv($user_id);
  660. $empLvSort = EmployLevel::getSortById($empLv);
  661. $empLvHighSort = EmployLevel::getSortById($highEmpLv);
  662. if ($empLvHighYear = YearHighestEmpLv::findOneAsArray('USER_ID=:USER_ID AND YEAR=:YEAR', [':USER_ID' => $user_id, ':YEAR' => $this->_calcYear], 'HIGHEST_EMP_LV_SORT')) {
  663. if ($empLvSort > $empLvHighYear['HIGHEST_EMP_LV_SORT']) {
  664. 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]);
  665. }
  666. } else {
  667. $yearHighest = new YearHighestEmpLv();
  668. $yearHighest->USER_ID = $user_id;
  669. $yearHighest->YEAR = $this->_calcYear;
  670. $yearHighest->HIGHEST_EMP_LV = $empLv;
  671. $yearHighest->HIGHEST_EMP_LV_SORT = $empLvSort;
  672. $yearHighest->HIGHEST_EMP_LV_PERIOD = $this->_periodNum;
  673. $yearHighest->CREATED_AT = Date::nowTime();
  674. if (!$yearHighest->save()) {
  675. throw new \yii\db\Exception(Form::formatErrorsForApi($yearHighest->getErrors()));
  676. }
  677. }
  678. if ($empLvSort > $empLvHighSort) {
  679. UserInfo::updateAll(['HIGHEST_EMP_LV' => $empLv, 'HIGHEST_EMP_LV_PERIOD' => $this->_periodNum], 'USER_ID=:USER_ID', [':USER_ID' => $user_id]);
  680. //发送历史最高聘级短信
  681. if($this->_sysConfig['smsEmpOpen']['VALUE']){
  682. if(in_array($empLvSort,explode(",",$this->_sysConfig['smsEmp']['VALUE']))){
  683. $realName = Info::getUserRealNameByUserId($user_id);
  684. $empLvDate = Date::convert();
  685. $empLvName = EmployLevel::getNameById($empLv);
  686. $content = str_replace(['{%REAL_NAME%}', '{%EMP_LV_DATE%}', '{%EMP_LV%}'], [$realName, $empLvDate, $empLvName], $this->_sysConfig['smsContent']['VALUE']);
  687. $params = [
  688. 'mobiles' => Info::getUserMobileByUserId($user_id),
  689. 'content' => $content,
  690. ];
  691. SmsApi::instance()->clearError();
  692. SmsApi::instance()->goSend($params);
  693. }
  694. }
  695. return true;
  696. }
  697. return false;
  698. }
  699. /**
  700. * 点位合作奖金
  701. * @param $userId
  702. * @param $amount
  703. * @param null $type
  704. * @return bool
  705. * @throws Exception
  706. * @throws \yii\db\Exception
  707. */
  708. private function _teamworkBonus($userId, $amount, $key) {
  709. 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;
  710. $fromUserInfo = CalcCache::getUserInfo($userId, $this->_periodNum);
  711. foreach ($teamwork as $value) {
  712. $bonus = Tool::formatPrice($amount * $value['DIVIDE_PERCENT'] * 0.01);
  713. if ($bonus <= 0) continue;
  714. $toUserInfo = CalcCache::getUserInfo($value['USER_ID'], $this->_periodNum);
  715. 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'] . '%']);
  716. 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'] . '%']);
  717. }
  718. }
  719. /**
  720. * 发送短信
  721. * @param int $offset
  722. * @return bool
  723. * @throws \yii\db\Exception
  724. */
  725. public function sendSmsLoop(int $offset = 0) {
  726. $allData = UserInfo::findUseDbCalc()->select('USER_ID,ALLOW_RECONSUME_SMS_TO')->where('ALLOW_RECONSUME_SMS=1')->offset($offset)->limit($this->_limit)->all();
  727. if ($allData) {
  728. $smsWallet = explode(",", $this->_sysConfig['smsWallet']['VALUE']);
  729. $smsFee = $this->_sysConfig['smsFee']['VALUE'];
  730. //获取剩余月份
  731. $period = Period::instance();
  732. $year = $period->getYear($this->_periodNum);
  733. $monthLeft = Period::getMonthLeft($this->_periodNum);
  734. $smsFee = Tool::formatPrice($smsFee * $monthLeft);
  735. $to = Date::yearEnd();
  736. $transaction = Yii::$app->db->beginTransaction();
  737. try {
  738. foreach ($allData as $data) {
  739. $userId = $data['USER_ID'];
  740. //过期续费
  741. if ($data['ALLOW_RECONSUME_SMS_TO'] < Date::nowTime()) {
  742. foreach ($smsWallet as $item) {
  743. //1奖金钱包2现金钱包
  744. if ($item == 1) {
  745. //看余额是否充足
  746. if (Balance::getAvailableBalance($userId) < $smsFee){
  747. UserInfo::updateAll(['ALLOW_RECONSUME_SMS' => 0], 'USER_ID=:USER_ID', [':USER_ID' => $userId]);
  748. continue;
  749. };
  750. Balance::changeUserBonus($userId, 'bonus', -abs($smsFee), ['DEAL_TYPE_ID' => DealType::SMS, 'REMARK' => $year . '年复销提醒短信服务费'], false);
  751. UserInfo::updateAll(['ALLOW_RECONSUME_SMS_TO' => $to], 'USER_ID=:USER_ID', [':USER_ID' => $userId]);
  752. $data['ALLOW_RECONSUME_SMS_TO'] = $to;
  753. break;
  754. } elseif ($item == 2) {
  755. throw new Exception('不存在此方式');
  756. break;
  757. }
  758. }
  759. }
  760. if ($data['ALLOW_RECONSUME_SMS_TO'] > Date::nowTime() && $mobile = Info::getUserMobileByUserId($userId)) {
  761. $realName = Info::getUserRealNameByUserId($userId);
  762. $reconsumPool = Reconsume::getUserReconsumePool($userId);
  763. $lastRechargeDate = $reconsumPool['toRechargeDate'];
  764. $isPass = $reconsumPool['isPass'] == 1 ? '合格' : '不合格';
  765. $month = $period->getNowMonth();
  766. $content = str_replace(['{%REAL_NAME%}', '{%LAST_RECHARGE_DATE%}', '{%MONTH%}', '{%IS_PASS%}'], [$realName, $lastRechargeDate, $month, $isPass], $this->_sysConfig['smsContent']['VALUE']);
  767. //todo 发短信函数 待测试
  768. $params = [
  769. 'mobiles' => $mobile,
  770. 'content' => $content,
  771. ];
  772. SmsApi::instance()->clearError();
  773. SmsApi::instance()->goSend($params);
  774. }
  775. unset($userId,$mobile,$realName,$reconsumPool,$lastRechargeDate,$isPass,$month,$content);
  776. }
  777. $transaction->commit();
  778. } catch (Exception $e) {
  779. $transaction->rollBack();
  780. return false;
  781. }
  782. unset($data);
  783. return $this->sendSmsLoop($offset + $this->_limit);
  784. }
  785. unset($allData);
  786. return true;
  787. }
  788. }