BonusSend.php 41 KB

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