userOperateLogger = new UserOperate([ 'fetchClass' => Transfer::class, ]); } /** * @inheritdoc */ public function rules() { return [ [['toUserName', 'toRealName', 'amount', 'type', 'remark'], 'trim'], [['toUserName', 'toRealName', 'amount', 'type', 'payPassword', 'transferCode'], 'required'], [['toUserName'], 'exist', 'targetClass' => UserInfo::class, 'targetAttribute' => 'USER_NAME'], [['type'], 'isType'], [['toUserName'], 'initUser'], [['amount'], 'price'], [['amount'], 'isAmount'], [['transferCode'], 'validateCode'], [['payPassword'], 'validatePassword'], ]; } /** * 指定校验场景 * @return array */ public function scenarios() { $parentScenarios = parent::scenarios(); $customScenarios = [ 'transfer' => ['type', 'toUserName', 'toRealName', 'amount', 'remark', 'payPassword', 'transferCode'], ]; return array_merge($parentScenarios, $customScenarios); } public function attributeLabels() { return [ 'toUserName' => 'Transferred Member Code', // 转入会员编号 'amount' => 'Amount', // 金额 'payPassword' => 'Payment Password', // 支付密码 'transferCode' => 'Verification Code', // 转账校验码 ]; } /** * 初始化转入转出用户信息 * @param $attribute * @return null * @throws Exception */ public function initUser($attribute) { // 转账记录 if(!$toUserId = Info::getUserIdByUserName($this->toUserName)){ $this->addError($attribute, 'Transferred member not exists'); // 转入会员不存在 return null; } // if(Info::getUserRealNameByUserId($toUserId)!=$this->toRealName){ // $this->addError($attribute, '转入会员会员姓名不正确'); // return null; // } $this->_toUserInfo = Info::baseInfo($toUserId); $fromUserId = \Yii::$app->user->id; $fromUser = Info::baseInfo($fromUserId); if (!$fromUser) { $this->addError($attribute, 'Transferred member not exists'); // 转入会员不存在 return null; } // 不能跨国转账 $fromCountryId = User::getEnCodeInfo($fromUserId)['COUNTRY_ID']; $toCountryId = User::getEnCodeInfo($toUserId)['COUNTRY_ID']; if ($fromCountryId != $toCountryId) { $this->addError($attribute, Yii::t('app', 'transferMemberDoesNotSame')); return null; } // 转账条件判断 $orderAmount = Order::find()->where('USER_ID=:USER_ID', [':USER_ID' => $fromUserId])->SUM('ORDER_AMOUNT'); $recNum = intval(DecOrder::find()->where('REC_USER_ID=:REC_USER_ID', [':REC_USER_ID' => $fromUserId])->count()); //$recNum = UserRelation::firstFloorChildNum($fromUserId); // if ($orderAmount < 300 && $recNum==0) { // $this->addError($attribute, '消费未满300元或未推荐新人,暂不能转账'); // return null; // } $this->_fromUserInfo = $fromUser; //是否同体系转账 // if($this->_transferConfig['isSystem']==1){ // if($this->_toUserInfo['SYSTEM_ID']!=$this->_fromUserInfo['SYSTEM_ID']){ // $this->addError($attribute, '只能同一体系间转账'); // return null; // } // } //是否点位绑定转账 // if ($this->_transferConfig['isBind'] == 1) { // if (!UserBind::sameBind($this->_toUserInfo['ID'], $this->_fromUserInfo['ID'])) { // $this->addError($attribute, '只能点位绑定在一起的会员之间转账'); // return null; // } // } //奖金转奖金限同一身份证 /*if ($this->type == self::BONUS_TO_BONUS) { $toIdCard = User::findOneAsArray('ID=:ID', [':ID' => $toUser['USER_ID']], 'ID_CARD'); $fromIdCard = User::findOneAsArray('ID=:ID', [':ID' => $fromUser['USER_ID']], 'ID_CARD'); if ($toIdCard['ID_CARD'] && ($toIdCard['ID_CARD'] != $fromIdCard['ID_CARD'])) { $this->addError($attribute, '奖金转奖金只能在同一身份证下进行'); return null; } }*/ //奖金转现金限一周一次 /*if ($this->type == self::BONUS_TO_BALANCE) { if (Transfer::hasThisWeekTransfer(\Yii::$app->user->id)) { $this->addError($attribute, '转账失败,每周只可以转账一次'); return null; } }*/ } /** * 校验申请金额是否小于当前余额并符合配置中的设置 * @param $attribute * @return null */ public function isAmount($attribute) { if(!$this->_fromUserInfo){ $this->addError($attribute, 'Member info has not been verified'); // 会员信息未验证通过 return null; } if ($this->amount <= 0) { $this->addError($attribute, 'The transfer amount must be greater than 0'); // 转账金额必须大于0 } if ((int)$this->amount!=$this->amount) { $this->addError('scenario', 'The transfer amount must be a round number'); // 转账金额必须是整数 } $minAmount = $this->_transferConfig['outMin']; if ($this->amount < $minAmount) { $this->addError($attribute, 'The transfer amount is lower than the transfer limit'); // 转账金额低于转账下限 } $maxAmount = $this->_transferConfig['outMax']; if ($maxAmount && $this->amount > $maxAmount) { $this->addError($attribute, 'The transfer amount is higher than the transfer limit'); // 转账金额高于转账上限 } //周转账上限 if($this->_transferConfig['weekMax']!=0){ if($this->amount+Transfer::weekTransfer($this->_fromUserInfo['ID'])>$this->_transferConfig['weekMax']){ $this->addError($attribute, 'The working book limit is exceeded'); // 超出周转账上限 } } //月转账上限 if($this->_transferConfig['monthMax']!=0){ if($this->amount+Transfer::monthTransfer($this->_fromUserInfo['ID'])>$this->_transferConfig['weekMax']){ $this->addError($attribute, 'Exceed the monthly transfer limit'); // 超出月转账上限 } } // 获取当前用户的可用金额 if( $this->type == self::BALANCE_TO_BALANCE ) { $haveBalance = Cash::getAvailableBalance($this->_fromUserInfo['ID']); }else { $haveBalance = Balance::getAvailableBalance($this->_fromUserInfo['ID']); } if ($this->amount > $haveBalance) { $this->addError($attribute, 'The transfer amount must be less than your available balance'); // 转账金额必须小于自己的可用余额 } // 转账比例 if ($this->type == self::BONUS_TO_BALANCE) { $isCanTransferProp = Cache::getSystemConfig()['isCanTransferProp']['VALUE']; if ($isCanTransferProp == 1) { $maxAmount = Tool::formatPrice($this->_fromUserInfo['TRANSFER_PROP'] * Balance::getAvailableBalance($this->_fromUserInfo['ID']) * 0.01); if ($this->amount > $maxAmount) { $this->addError($attribute, 'The transfer amount exceeds the transfer ratio limit'); // 转账金额超出转账比例限额 } } } //手续费 if($this->_transferConfig['fee']!=0){ $fee = Tool::formatPrice($this->amount * ($this->_transferConfig['fee']) * 0.01); if($this->_transferConfig['feeMin']!=0&&$fee<$this->_transferConfig['feeMin']){ $fee = $this->_transferConfig['feeMin']; } if($this->_transferConfig['feeMax']!=0&&$fee>$this->_transferConfig['feeMax']){ $fee = $this->_transferConfig['feeMax']; } $this->_fee = $fee; } } /** * 转账类型判断 * @param $attribute */ public function isType($attribute) { if (!in_array($this->type, self::allowTransferType)) { $this->addError($attribute, 'Types of transfers that are not allowed'); // 不允许的转账类型 } //获取转账参数 if(!$this->_transferConfig = Json::decode(Cache::getSystemConfig()['allowWallet']['VALUE'])[$this->type-1]){ $this->addError($attribute, 'Wrong type of transfer'); // 错误的转账类型 } if(!$this->_transferConfig['isOpen']){ $this->addError($attribute, 'Type of transfer that is not available'); // 不可用的转账类型 } } /** * 校验支付密码 * @param $attribute * @param $params */ public function validatePassword($attribute, $params) { if (!User::validatePayPassword($this->_fromUserInfo['ID'], $this->payPassword)) { $this->addError($attribute, 'The payment password is incorrect');//支付密码不正确 } } public function validateCode($attribute, $params) { $uid = \Yii::$app->user->id; $redisCode = \Yii::$app->redis->getset('transferCode_'.$uid,''); \Yii::$app->redis->del('transferCode_'.$uid); if ($this->transferCode!=$redisCode) { $this->addError($attribute, 'Transfer verification failed '.$redisCode); // 转账校验失败 } } /** * 生成随机数 * @param $length * @param int $numeric * @return string */ public function random($length, $numeric = 0) { $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed)); $hash = ''; $max = strlen($seed) - 1; for ($i = 0; $i < $length; $i++) { $hash .= $seed[mt_rand(0, $max)]; } return $hash; } /** * 转账 * @return Transfer|null * @throws \Exception */ public function transfer() { if (!$this->validate()) { return null; } if ($this->_fromUserInfo['ID']==$this->_toUserInfo['ID']){ throw new \Exception(Yii::t('app', 'notAllowedToTransferToYourself')); } $fromData = Balance::getLogData($this->_fromUserInfo['ID']); $toData = Balance::getLogData($this->_toUserInfo['ID']); $this->userOperateLogger->saveBeforeContent=array_merge($fromData,$toData); $db = \Yii::$app->db; $transaction = $db->beginTransaction(); try { $amount = Tool::formatPrice($this->amount - $this->_fee); // 转账记录 $period = Period::instance(); $model = new Transfer(); $model->ID = SnowFake::instance()->generateId(); $model->TRANSFER_SN = Transfer::generateSN(); $model->OUT_UID = $this->_fromUserInfo['ID']; $model->LAST_OUT_USER_NAME = $this->_fromUserInfo['USER_NAME']; $model->LAST_OUT_REAL_NAME = $this->_fromUserInfo['REAL_NAME']; $model->LAST_OUT_DEC_LV = $this->_fromUserInfo['DEC_LV']; $model->LAST_OUT_DEC_ROLE_ID = $this->_fromUserInfo['DEC_ROLE_ID']; $model->OUT_WALLET = $this->_transferConfig['out']; $model->LAST_OUT_SYSTEM_ID = $this->_fromUserInfo['SYSTEM_ID'] ? $this->_fromUserInfo['SYSTEM_ID'] : ''; $model->IN_UID = $this->_toUserInfo['ID']; $model->LAST_IN_USER_NAME = $this->_toUserInfo['USER_NAME']; $model->LAST_IN_REAL_NAME = $this->_toUserInfo['REAL_NAME']; $model->LAST_IN_DEC_LV = $this->_toUserInfo['DEC_LV']; $model->IN_WALLET = $this->_transferConfig['in']; $model->LAST_IN_SYSTEM_ID = $this->_toUserInfo['SYSTEM_ID'] ? $this->_toUserInfo['SYSTEM_ID'] : ''; $model->ORI_AMOUNT = $this->amount; $model->FEE = $this->_fee; $model->AMOUNT = $amount; $model->REMARK = $this->remark; $model->PERIOD_NUM = $period->getNowPeriodNum(); $model->CALC_MONTH = $period->getNowYearMonth(); $model->CREATED_AT = Date::nowTime(); if (!$model->save()) { throw new Exception(Form::formatErrorsForApi($model->getErrors())); } if ($this->type == self::BONUS_TO_BALANCE) { // 会员金额减少 Balance::changeUserBonus($this->_fromUserInfo['ID'], 'bonus', -abs($this->amount), ['TRANSFER_SN' => $model->TRANSFER_SN, 'DEAL_TYPE_ID' => DealType::TRANSFER_OUT, 'REMARK' => 'To:' . $this->_toUserInfo['USER_NAME'] . ',' . $this->remark]); Cash::changeUserCash($this->_toUserInfo['ID'], 'CASH', abs($amount), ['TRANSFER_SN' => $model->TRANSFER_SN,'DEAL_TYPE_ID' => DealType::TRANSFER_IN, 'REMARK' => 'From:' . $this->_fromUserInfo['USER_NAME'] . ',' . $this->remark]); } elseif($this->type == self::BONUS_TO_BONUS) { // 会员金额减少 Balance::changeUserBonus($this->_fromUserInfo['ID'], 'bonus', -abs($this->amount), ['TRANSFER_SN' => $model->TRANSFER_SN, 'DEAL_TYPE_ID' => DealType::TRANSFER_OUT, 'REMARK' => 'To:' . $this->_toUserInfo['USER_NAME'] . ',' . $this->remark]); Balance::changeUserBonus($this->_toUserInfo['ID'], 'bonus', abs($amount), ['TRANSFER_SN' => $model->TRANSFER_SN, 'DEAL_TYPE_ID' => DealType::TRANSFER_IN, 'REMARK' => 'From:' . $this->_fromUserInfo['USER_NAME'] . ',' . $this->remark]); }else if ($this->type == self::BALANCE_TO_BALANCE) { // 会员金额减少 Cash::changeUserCash($this->_fromUserInfo['ID'], 'CASH', -abs($this->amount), ['TRANSFER_SN' => $model->TRANSFER_SN,'DEAL_TYPE_ID' => DealType::TRANSFER_OUT, 'REMARK' => 'To:' . $this->_toUserInfo['USER_NAME'] . ',' . $this->remark]); Cash::changeUserCash($this->_toUserInfo['ID'], 'CASH', abs($amount), ['TRANSFER_SN' => $model->TRANSFER_SN,'DEAL_TYPE_ID' => DealType::TRANSFER_IN, 'REMARK' => 'From:' . $this->_fromUserInfo['USER_NAME'] . ',' . $this->remark]); }else { throw new \Exception(Yii::t('app', 'wrongTransactionType')); } $transaction->commit(); } catch (Exception $e) { $transaction->rollBack(); $this->addError('edit', $e->getMessage()); return null; } $fromData = Balance::getLogData($this->_fromUserInfo['ID']); $toData = Balance::getLogData($this->_toUserInfo['ID']); $this->userOperateLogger->saveAfterContent=array_merge($fromData,$toData); unset($fromData,$toData); $this->userOperateLogger->clean()->save([ 'optType' => '会员转账', 'userId' => \Yii::$app->user->id, 'userName' => Info::getUserNameByUserId(\Yii::$app->user->id), 'remark' => $this->remark, ]); return $model; } }