[ 'id' => 'userBonus', 'title' => '奖金', 'attr' => 'userBonus', 'pv' => false, ], 'userPerformanceBonus' => [ 'id' => 'userPerformanceBonus', 'title' => '绩效奖金', 'attr' => 'userPerformanceBonus', 'pv' => false, ], ]; /** * 发票流水 * @param $userId * @param $amount * @param $params * @return bool * @throws Exception * @throws \yii\db\Exception */ public static function changeInvoice($userId, $amount, $params) { if ($amount == 0) return true; $period = Period::instance(); if (!isset($params['PERIOD_NUM'])) { $periodNum = $period->getNowPeriodNum(); } else { $periodNum = $params['PERIOD_NUM']; } $calcYearMonth = $period->getYearMonth($periodNum); // redis加锁(防止并发余额数值不准确出错) $lockKey = self::INVOICE_BALANCE_LOCK_KEY . $userId; if (RedisLock::instance()->lock($lockKey)) { $userInfo = UserInfo::findOne(['USER_ID' => $userId]); $totals = $userInfo->INVOICE_BALANCE + $amount; $userInfo->INVOICE_BALANCE = $totals; if (!$userInfo->save()) { throw new \Exception(Form::formatErrorsForApi($userInfo->getErrors())); } //记录流水 $baseInfo = Info::baseInfoZh($userId); $flowInsertData = [ 'USER_ID' => $userId, 'REAL_NAME' => $baseInfo['REAL_NAME'], 'DEC_LV' => $baseInfo['DEC_LV'], 'EMP_LV' => $baseInfo['EMP_LV'], 'MOBILE' => $baseInfo['MOBILE'], 'REG_TYPE' => $userInfo['REG_TYPE'], 'REG_NAME' => $userInfo['REG_NAME'], 'CREDIT_CODE' => $userInfo['CREDIT_CODE'], 'SALE_NAME' => $params['SALE_NAME'] ?? null, 'TAXPAYER_NUMBER' => $params['TAXPAYER_NUMBER'] ?? null, 'INVOICE_SN' => $params['INVOICE_SN'] ?? null, 'INVOICE_ACCOUNT' => $params['INVOICE_ACCOUNT'] ?? null, 'TAX_ACCOUNT' => $params['TAX_ACCOUNT'] ?? null, 'OUTED_AT' => $params['OUTED_AT'] ?? 0, 'AMOUNT' => abs($amount), 'TOTAL' => $totals, 'WITHDRAW_SN' => $params['WITHDRAW_SN'] ?? null, 'IS_INCR' => $amount > 0 ? self::INCR_ADD : self::INCR_REDUCE, 'PERIOD_NUM' => $periodNum, 'CALC_MONTH' => $calcYearMonth, 'REMARK' => $params['REMARK'] ?? null, 'CREATE_ADMIN' => $params['CREATE_ADMIN'], 'CREATE_REMARK' => $params['CREATE_REMARK'] ?? null, 'CREATE_TIME' => $params['CREATE_TIME'], 'AUDIT_ADMIN' => $params['AUDIT_ADMIN'], 'AUDIT_REMARK' => $params['AUDIT_REMARK'] ?? null, 'AUDIT_TIME' => $params['AUDIT_TIME'], 'P_MONTH' => Date::ociToDate(), 'CREATED_AT' => $params['TIME'] ?? Date::nowTime(), ]; InvoiceFlow::insertOne($flowInsertData); unset($flowInsertData); RedisLock::instance()->unlock($lockKey); } else { throw new Exception('流水产生错误'); } return true; } /** * 获取当前可用余额 * @param $userId * @return int|mixed */ public static function getAvailableBalance($userId) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); if ($oneData) { return $oneData['BONUS'] - $oneData['BONUS_FREEZE']; } else { return 0; } } /** * 获取当前车房养老奖余额 * @param $userId * @return int|mixed */ public static function getBalanceCF($userId) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); if ($oneData) { return $oneData['CF']; } else { return 0; } } /** * 获取当前复消积分余额 * @param $userId * @return int|mixed */ public static function getBalanceReconsumePoints($userId) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); if ($oneData) { return $oneData['RECONSUME_POINTS']; } else { return 0; } } /** * 获取当前兑换积分余额 * @param $userId * @return int|mixed */ public static function getBalanceExchangePoints($userId) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); if ($oneData) { return $oneData['EXCHANGE_POINTS']; } else { return 0; } } /** * 获取当前车房养老奖余额 * @param $userId * @return int|mixed */ public static function getBalanceLX($userId) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); if ($oneData) { return $oneData['LX']; } else { return 0; } } /** * 获取当前旅游积分余额 * @param $userId * @return int|mixed */ public static function getBalanceTourism($userId) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); return $oneData['TOURISM_POINTS'] ?? 0; } /** * 获取当前车房余额 * @param $userId * @return int|mixed */ public static function getBalanceGarage($userId) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); return $oneData['GARAGE_POINTS'] ?? 0; } /** * 查询会员账户余额. * @param $userId * @param $payType * @return int|mixed */ public static function getAccountBalance($userId, $payType) { $oneData = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); return !$oneData ? 0 : $oneData[Balance::BALANCE_TYPE[$payType]['attr']] ?? 0; } /** * 改变会员的余额 * @param $userId * @param $type * @param $amount * @param array $params * @param bool $allowMinus * @return bool * @throws Exception * @throws \yii\db\Exception */ public static function changeUserBonus($userId, $type, $amount, $params = [], $allowMinus = false): bool { if (array_key_exists($type, UserBonus::TYPE)) { $type = strtoupper($type); } if ($amount == 0) return true; $period = Period::instance(); if (!isset($params['PERIOD_NUM'])) { $periodNum = $period->getNowPeriodNum(); } else { $periodNum = $params['PERIOD_NUM']; } $calcYearMonth = $period->getYearMonth($periodNum); // 汇率 // $countryId = User::getEnCodeInfo($userId)['COUNTRY_ID']; // $decCountry = Countries::getById($countryId); // $exchangeRate = CurrencyConversions::getToUSDRate($decCountry['LOCAL_CURRENCY_ID']); $exchangeRate = 1; // TODO:奖金发放美元 // redis加锁(防止并发余额数值不准确出错) switch ($type) { case 'BONUS': $lockKey = self::BONUS_BALANCE_LOCK_KEY . $userId; break; default: throw new Exception('流水类型错误'); } if (RedisLock::instance()->lock($lockKey)) { // 改变发奖 $paramData = []; $oneUserBonusModel = UserBonus::findOne(['USER_ID' => $userId]); // 是否奖金发放操作 $issueBonus = $params['BONUS_ISSUE'] ?? false; if ($oneUserBonusModel) { $paramData[$type] = new Expression($type.' + ' . ($issueBonus ? $amount * $exchangeRate : $amount)); $oneUserBonusModel->$type += ($issueBonus ? $amount * $exchangeRate : $amount); if ($oneUserBonusModel->$type < 0) { RedisLock::instance()->unlock($lockKey); throw new Exception('金额不足'); } if (isset($params['BONUS_TOTAL'])) { $paramData['BONUS_TOTAL'] = new Expression('BONUS_TOTAL + ' . ($issueBonus ? $params['BONUS_TOTAL'] * $exchangeRate : $params['BONUS_TOTAL'])); } // 奖金发放 UserBonus::updateAll($paramData, 'USER_ID=:USER_ID', [':USER_ID' => $userId]); } else { $paramData = [ 'USER_ID' => $userId, $type => $issueBonus ? $amount * $exchangeRate : $amount, 'CREATED_AT' => Date::nowTime() ]; if (isset($params['MANAGE_TAX'])) { $paramData['MANAGE_TAX'] = ($issueBonus ? $params['MANAGE_TAX'] * $exchangeRate : $params['MANAGE_TAX']); } if (isset($params['BONUS_TOTAL'])) { $paramData['BONUS_TOTAL'] = ($issueBonus ? $params['BONUS_TOTAL'] * $exchangeRate : $params['BONUS_TOTAL']); } // 新增用户奖金记录 UserBonus::insertOne($paramData); } unset($oneUserBonusModel,$paramData); // 获取发放完成的奖金信息 $oneUserBonus = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); $userInfo = Info::getLastInfo($userId); // 记录流水 $flowInsertData = [ 'USER_ID' => $userId, 'LAST_DEC_LV' => $userInfo['DEC_LV'], 'LAST_EMP_LV' => $userInfo['LAST_EMP_LV'], 'LAST_STATUS' => $userInfo['STATUS'], 'CALC_ID' => $params['CALC_ID'] ?? null, 'AMOUNT' => ($issueBonus ? $amount * $exchangeRate : $amount), 'AMOUNT_STANDARD' => (!$issueBonus ? $amount / $exchangeRate : $amount), 'EXCHANGE_RATE' => $exchangeRate, 'TOTAL' => $oneUserBonus[$type], 'IS_INCR' => $amount > 0 ? FlowBonus::INCR_ADD : FlowBonus::INCR_REDUCE, 'REMARK' => $params['REMARK'] ?? null, 'REMARK_IS_SHOW' => $params['REMARK_IS_SHOW'] ?? 1, 'PERIOD_NUM' => $params['PERIOD_NUM'] ?? $periodNum, 'CALC_MONTH' => $calcYearMonth, 'P_MONTH' => Date::ociToDate(), 'CREATED_AT' => $params['TIME'] ?? Date::nowTime(), 'ADMIN_NAME' => $params['ADMIN_NAME'] ?? 'system', 'DEAL_TYPE_ID' => $params['DEAL_TYPE_ID'] ?? '', 'DEAL_TYPE_IS_PRESET' => $params['DEAL_TYPE_IS_PRESET'] ?? 1, 'TRANSFER_SN' => $params['TRANSFER_SN'] ?? '', 'SORT' => $params['SORT'] ?? 0, 'ORDER_SN' => $params['ORDER_SN'] ?? '', ]; // 写入流水 if (strtolower($type) == 'bonus') { FlowBonus::insertOne($flowInsertData); } unset($flowInsertData, $userInfo, $oneUserBonus); RedisLock::instance()->unlock($lockKey); } else { throw new Exception('流水产生错误'); } return true; } /** * 添加对应期数的复消积分 * @param $userId * @param $periodNum * @param $amount * @throws \yii\db\Exception * @return boolean */ public static function addPeriodReconsumePoints($userId, $periodNum, $amount) { if($amount <= 0) return false; $exists = UserPeriodPoints::find()->where('USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM', [ 'USER_ID' => $userId, 'PERIOD_NUM' => $periodNum, ])->asArray()->exists(); if( $exists ) { UserPeriodPoints::updateAllCounters([ 'RECONSUME_POINTS' => $amount, 'REMAINDER_POINTS' => $amount, ], 'USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM', [ 'USER_ID' => $userId, 'PERIOD_NUM' => $periodNum, ]); }else { UserPeriodPoints::insertOne([ 'USER_ID' => $userId, 'PERIOD_NUM' => $periodNum, 'RECONSUME_POINTS' => $amount, 'REMAINDER_POINTS' => $amount, 'EXPIRED' => 0, 'CREATED_AT' => Date::nowTime() ]); } return true; } /** * 添加对应期数的兑换积分 * @param $userId * @param $periodNum * @param $amount * @throws \yii\db\Exception * @return boolean */ public static function addPeriodExchangePoints($userId, $periodNum, $amount) { if($amount <= 0) return false; $exists = UserPeriodExchangePoints::find()->where('USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM', [ 'USER_ID' => $userId, 'PERIOD_NUM' => $periodNum, ])->asArray()->exists(); if( $exists ) { UserPeriodExchangePoints::updateAllCounters([ 'EXCHANGE_POINTS' => $amount, 'REMAINDER_POINTS' => $amount, ], 'USER_ID=:USER_ID AND PERIOD_NUM=:PERIOD_NUM', [ 'USER_ID' => $userId, 'PERIOD_NUM' => $periodNum, ]); }else { UserPeriodExchangePoints::insertOne([ 'USER_ID' => $userId, 'PERIOD_NUM' => $periodNum, 'EXCHANGE_POINTS' => $amount, 'REMAINDER_POINTS' => $amount, 'EXPIRED' => 0, 'CREATED_AT' => Date::nowTime() ]); } return true; } /** * 减少 * @param $userId * @param $amount * @return bool */ public static function deductPeriodReconsumePoints($userId, $amount) { if( $amount <= 0 ) return false; $avalidList = UserPeriodPoints::find()->where('USER_ID=:USER_ID AND EXPIRED=:EXPIRED AND REMAINDER_POINTS>0', [ 'USER_ID' => $userId, 'EXPIRED'=>0 ])->orderBy('PERIOD_NUM ASC')->asArray()->all(); if( !$avalidList ) return false; foreach ($avalidList as $everyData) { if( $amount <= 0 ) break; $remainderPoints = floatval($everyData['REMAINDER_POINTS']); if( $amount >= $remainderPoints ) { UserPeriodPoints::updateAllCounters([ 'REMAINDER_POINTS' => (-1) * $remainderPoints ], 'ID=:ID', ['ID'=>$everyData['ID']]); $amount -= $remainderPoints; }else { UserPeriodPoints::updateAllCounters([ 'REMAINDER_POINTS' => (-1) * $amount ], 'ID=:ID', ['ID'=>$everyData['ID']]); $amount = 0; } unset($everyData, $remainderPoints); } if( $amount > 0 ) return false; return true; } /** * 减少 * @param $userId * @param $amount * @return bool */ public static function deductPeriodExchangePoints($userId, $amount) { if( $amount <= 0 ) return false; $avalidList = UserPeriodExchangePoints::find()->where('USER_ID=:USER_ID AND EXPIRED=:EXPIRED AND REMAINDER_POINTS>0', [ 'USER_ID' => $userId, 'EXPIRED'=>0 ])->orderBy('PERIOD_NUM ASC')->asArray()->all(); if( !$avalidList ) return false; foreach ($avalidList as $everyData) { if( $amount <= 0 ) break; $remainderPoints = floatval($everyData['REMAINDER_POINTS']); if( $amount >= $remainderPoints ) { UserPeriodExchangePoints::updateAllCounters([ 'REMAINDER_POINTS' => (-1) * $remainderPoints ], 'ID=:ID', ['ID'=>$everyData['ID']]); $amount -= $remainderPoints; }else { UserPeriodExchangePoints::updateAllCounters([ 'REMAINDER_POINTS' => (-1) * $amount ], 'ID=:ID', ['ID'=>$everyData['ID']]); $amount = 0; } unset($everyData, $remainderPoints); } if( $amount > 0 ) return false; return true; } /** * 冻结用户余额 * @param $userId * @param $amount * @param null $remark * @param null $time * @return bool * @throws Exception * @throws \yii\db\Exception */ public static function freezeUserBonus($userId, $amount, $remark = null, $time = null) { return self::changeFreezeUserBonus($userId, $amount, ['REMARK' => $remark, 'TIME' => $time]); } /** * 解冻用户余额 * @param $userId * @param $amount * @param null $remark * @param null $time * @return bool * @throws Exception * @throws \yii\db\Exception */ public static function unfreezeUserBonus($userId, $amount, $remark = null, $time = null) { return self::changeFreezeUserBonus($userId, -$amount, ['REMARK' => $remark, 'TIME' => $time]); } /** * @param $userId * @param $amount * @param $params * [ * 'REMARK' => '备注', * 'PERIOD_NUM' => 100, * ] * @return bool * @throws Exception * @throws \yii\db\Exception */ public static function changeFreezeUserBonus($userId, $amount, $params) { if ($amount == 0) return true; $period = Period::instance(); if (!isset($params['PERIOD_NUM'])) { $periodNum = $period->getNowPeriodNum(); } else { $periodNum = $params['PERIOD_NUM']; } $calcYearMonth = $period->getYearMonth($periodNum); // 改变冻结 $oneUserBonusModel = UserBonus::findOne(['USER_ID' => $userId]); if ($oneUserBonusModel) { $oneUserBonusModel->BONUS_FREEZE = $oneUserBonusModel->BONUS_FREEZE + $amount; } else { $oneUserBonusModel = new UserBonus(); $oneUserBonusModel->USER_ID = $userId; $oneUserBonusModel->BONUS_FREEZE = $amount; } if (!$oneUserBonusModel->save()) { throw new Exception(Form::formatErrorsForApi($oneUserBonusModel->getErrors())); } unset($oneUserBonusModel); // 流水 // 获取发放完成的奖金信息 $oneUserBonus = UserBonus::find()->where('USER_ID=:USER_ID', [':USER_ID' => $userId])->asArray()->one(); // 记录流水 $flowInsertData = [ 'USER_ID' => $userId, 'AMOUNT' => abs($amount), 'TOTAL' => $oneUserBonus['BONUS'], 'IS_INCR' => $amount > 0 ? FlowBonus::INCR_FREEZE : FlowBonus::INCR_UNFREEZE, 'REMARK' => $params['REMARK'] ?? null, 'PERIOD_NUM' => $params['PERIOD_NUM'] ?? $periodNum, 'CALC_MONTH' => $calcYearMonth, 'P_MONTH' => Date::ociToDate(), 'CREATED_AT' => $params['TIME'] ?? Date::nowTime(), ]; FlowBonus::insertOne($flowInsertData); unset($flowInsertData); return true; } /** * 清空会员奖金有流水 * @param $userId * @param array $params * @throws Exception * @throws \yii\db\Exception */ public static function clearAllBonus($userId, $params = []) { // 先查找会员的全部余额 $userBonus = UserBonus::findOne(['USER_ID' => $userId]); // 如果没有会员余额数据,新建余额数据 if (!$userBonus) { UserBonus::insertOne(['USER_ID' => $userId, 'CREATED_AT' => Date::nowTime()]); } else { $period = Period::instance(); foreach (\Yii::$app->params['bonusWalletType'] as $type) { $field = strtoupper($type['name']); if ($userBonus[$field]<=0) continue; $userInfo = Info::getLastInfo($userId); $flowInsertData = [ 'USER_ID' => $userId, 'LAST_DEC_LV' => $userInfo['DEC_LV'], 'LAST_EMP_LV' => $userInfo['EMP_LV'], 'LAST_STATUS' => $userInfo['STATUS'], 'CALC_ID' => $params['CALC_ID'] ?? null, 'AMOUNT' => -$userBonus[$field], 'TOTAL' => 0, 'IS_INCR' => FlowBonus::INCR_REDUCE, 'REMARK' => $params['REMARK'] ?? null, 'REMARK_IS_SHOW' => $params['REMARK_IS_SHOW'] ?? 1, 'PERIOD_NUM' => $params['PERIOD_NUM'] ?? $period->getNowPeriodNum(), 'CALC_MONTH' => $period->getNowYearMonth(), 'P_MONTH' => Date::ociToDate(), 'CREATED_AT' => $params['TIME'] ?? Date::nowTime(), 'ADMIN_NAME' => $params['ADMIN_NAME'] ?? 'system', 'DEAL_TYPE_ID' => $params['DEAL_TYPE_ID'] ?? '', 'DEAL_TYPE_IS_PRESET' => $params['DEAL_TYPE_IS_PRESET'] ?? 1, 'TRANSFER_SN' => $params['TRANSFER_SN'] ?? '', 'SORT' => $params['SORT'] ?? 0, ]; // 流水 if (strtolower($field) == 'bonus') FlowBonus::insertOne($flowInsertData); elseif (strtolower($field) == 'cf') { unset($flowInsertData['CALC_ID']); unset($flowInsertData['SORT']); unset($flowInsertData['TRANSFER_SN']); FlowCF::insertOne($flowInsertData); } elseif (strtolower($field) == 'lx') { unset($flowInsertData['CALC_ID']); unset($flowInsertData['SORT']); unset($flowInsertData['TRANSFER_SN']); FlowLX::insertOne($flowInsertData); } } // 清空 $userBonus->BONUS = 0; $userBonus->CF = 0; $userBonus->LX = 0; if (!$userBonus->save()) { throw new Exception(Form::formatErrorsForApi($userBonus->getErrors())); } } FlowBonus::updateAll(['DELETED' => 1, 'DELETED_AT' => Date::nowTime()], 'USER_ID=:USER_ID', [':USER_ID' => $userId]); FlowCF::updateAll(['DELETED' => 1, 'DELETED_AT' => Date::nowTime()], 'USER_ID=:USER_ID', [':USER_ID' => $userId]); FlowLX::updateAll(['DELETED' => 1, 'DELETED_AT' => Date::nowTime()], 'USER_ID=:USER_ID', [':USER_ID' => $userId]); } /** * 是否存在奖金余额 * @param $userId * @return bool */ public static function hasBonus($userId) { $userBonus = UserBonus::findOne(['USER_ID' => $userId]); if (!$userBonus) { return false; } foreach (\Yii::$app->params['bonusWalletType'] as $type) { $field = strtoupper($type['name']); if (isset($userBonus[$field]) && $userBonus[$field] > 0) { return true; } } return false; } /** * 获取金额用于日志 * @param $userId * @return array */ public static function getLogData($userId){ $userWallet = UserWallet::findOne(['USER_ID' => $userId]); $cash = !empty($userWallet) ? $userWallet['CASH'] : ''; $userName = Info::getUserNameByUserId($userId); $data = []; $data[$userId]['label'] = $userName.'余额'; $data[$userId]['value'] = '奖金'.self::getAvailableBalance($userId).',现金'.$cash; return $data; } }