32], [['USER_ID'], 'unique'], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'ID' => 'ID', 'USER_ID' => '用户id', 'AMOUNTS' => '当前余额', 'ORIGINAL' => '原始金额', 'UPDATED_AT' => '修改时间', ]; } /** * 获取一名会员的余额 * @param $userId * @return array|null */ public static function getAmountByUserId($userId) { $data = UserPerformance::find()->select('SUM(AMOUNTS) AS AMOUNTS')->where('USER_ID=:USER_ID AND STATUS_ID<(:STATUS_ID)', [':USER_ID' => $userId, ':STATUS_ID' => self::FINISHED])->asArray()->one(); if(!$data){ $data = [ 'USER_ID' => $userId, 'AMOUNTS' => 0, ]; } return $data; } public static function getAmounts($userId) { $data = UserPerformance::find()->select('SUM(AMOUNTS) AS AMOUNTS')->where('USER_ID=:USER_ID AND STATUS_ID<(:STATUS_ID)', [':USER_ID' => $userId, ':STATUS_ID' => self::FINISHED])->asArray()->one(); return $data['AMOUNTS'] ?? 0; } /** * @param $userId * @param $amount * @param string $orderId * @return true * @throws Exception * @throws \yii\db\Exception */ public static function changeUserPerformance($userId, $amount, string $orderId = ''): bool { if ($amount == 0) { return true; } // 会员总绩效奖金 $userPerformanceAmount = self::getAmountByUserId($userId); if ($amount > $userPerformanceAmount) { throw new Exception(Yii::t('app', 'applicantPrpShort')); } $period = Period::instance(); $periodNum = $period->getNowPeriodNum(); $calcYearMonth = $period->getYearMonth($periodNum); // redis加锁(防止并发余额数值不准确出错) $lockKey = self::USER_PERFORMANCE_BALANCE_LOCK_KEY . $userId; if (RedisLock::instance()->lock($lockKey)) { // 根据规则获取绩效奖金:过期时间(升序) -> 发放时间(升序) $records = self::find() ->where('USER_ID=:USER_ID AND AMOUNTS>0 AND STATUS_ID<:STATUS_ID', [':USER_ID' => $userId, ':STATUS_ID' => self::FINISHED]) ->orderBy('EXPIRED_AT ASC, CREATED_AT ASC') ->asArray() ->all(); $db = \Yii::$app->db; $transaction = $db->beginTransaction(); try { // 循环扣除绩效奖金 $surplus = $amount; foreach ($records as $record) { $balance = 0; if ($record['AMOUNTS'] > $surplus) { $balance = $record['AMOUNTS'] - $surplus; // 扣除奖金 UserPerformance::updateAll(['AMOUNTS' => $balance, 'STATUS_ID' => self::USING], 'ID=:ID', [':ID' => $record['ID']]); // 写日志 UserPerformanceLogs::changeAmountLogs($record['ID'], $surplus, $periodNum, $orderId); break; } if ($record['AMOUNTS'] == $surplus) { $balance = $record['AMOUNTS'] - $surplus; // 扣除奖金 UserPerformance::updateAll(['AMOUNTS' => $balance, 'STATUS_ID' => self::FINISHED], 'ID=:ID', [':ID' => $record['ID']]); // 写日志 UserPerformanceLogs::changeAmountLogs($record['ID'], $surplus, $periodNum, $orderId); break; } if ($record['AMOUNTS'] < $surplus) { $balance = $surplus - $record['AMOUNTS']; $surplus = $balance; // 扣除奖金 UserPerformance::updateAll(['AMOUNTS' => 0, 'STATUS_ID' => self::FINISHED], 'ID=:ID', [':ID' => $record['ID']]); // 写日志 UserPerformanceLogs::changeAmountLogs($record['ID'], $surplus, $periodNum, $orderId); } } $transaction->commit(); } catch (\Exception $e) { $transaction->rollBack(); throw new Exception($e->getMessage()); } // 解除锁 RedisLock::instance()->unlock($lockKey); } else { throw new Exception(Yii::t('app', 'flowCreateError')); } return true; } /** * 绩效奖金发放 * @param $userId * @param $amount * @param $bountyPeriodNum * @return bool * @throws Exception */ public static function sentUserPerformance($userId, $amount, $bountyPeriodNum): bool { if ($amount == 0) { return true; } // 会员国家 $countryCode = Countries::getById(User::getEnCodeInfo($userId)['COUNTRY_ID'])['CODE']; $period = Period::instance(); $periodNum = $period->getNowPeriodNum(); $db = \Yii::$app->db; $transaction = $db->beginTransaction(); try { // 奖金发放 $id = 'EP' . Tool::generateUserPerformanceNo($countryCode); self::insertOne([ 'ID' => $id, 'USER_ID' => $userId, 'AMOUNTS' => $amount, 'ORIGINAL' => $amount, 'STATUS_ID' => self::NEWS, 'EXPIRED_AT' => date('Y-m-d H:i:s', strtotime('+1 year', time())), 'CREATED_AT' => date('Y-m-d H:i:s', time()), 'UPDATED_AT' => date('Y-m-d H:i:s', time()), 'REMARK' => DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND), 'BOUNTY_PERIOD_NUM' => $bountyPeriodNum, 'PAID_PERIOD_NUM' => $periodNum, ]); // 写日志 UserPerformanceLogs::changeAmountLogs($id, $amount, $periodNum, '', DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND)); $transaction->commit(); } catch (\Exception $e) { $transaction->rollBack(); throw new Exception(sprintf('PB奖金挂网异常:File: %s, Line: {%s}, message: %s', $e->getFile(), $e->getLine(), $e->getMessage())); } return true; } /** * 绩效奖金发放 * @param $userId * @param $amount * @param $bountyPeriodNum * @return bool * @throws Exception */ public static function sentUserPerformanceApi($userId, $amount, $bountyPeriodNum): bool { if ($amount == 0) { return true; } // 会员国家 $countryCode = Countries::getById(User::getEnCodeInfo($userId)['COUNTRY_ID'])['CODE']; $db = \Yii::$app->db; $transaction = $db->beginTransaction(); try { // 奖金发放 $id = 'EP' . Tool::generateUserPerformanceNo($countryCode); self::insertOne([ 'ID' => $id, 'USER_ID' => $userId, 'AMOUNTS' => $amount, 'ORIGINAL' => $amount, 'STATUS_ID' => self::NEWS, 'EXPIRED_AT' => date('Y-m-d H:i:s', strtotime('+1 year', time())), 'CREATED_AT' => date('Y-m-d H:i:s', time()), 'UPDATED_AT' => date('Y-m-d H:i:s', time()), 'REMARK' => DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND), 'BOUNTY_PERIOD_NUM' => $bountyPeriodNum, 'PAID_PERIOD_NUM' => $bountyPeriodNum+1, ]); // 写日志 UserPerformanceLogs::changeAmountLogs($id, $amount, $bountyPeriodNum+1, '', DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND)); $transaction->commit(); } catch (\Exception $e) { $transaction->rollBack(); throw new Exception(sprintf('PB奖金挂网异常:File: %s, Line: {%s}, message: %s', $e->getFile(), $e->getLine(), $e->getMessage())); } return true; } }