// +---------------------------------------------------------------------- namespace App\Services\Api; use App\Helpers\Jwt; use App\Models\AccountLogModel; use App\Models\BalanceLogModel; use App\Models\MemberLevelModel; use App\Models\MemberModel; use App\Services\BaseService; use App\Services\ConfigService; use App\Services\RedisService; use App\Services\ToolService; use App\Services\WalletService; use Illuminate\Support\Facades\DB; /** * 会员-服务类 * @author laravel开发员 * @since 2020/11/11 * @package App\Services\Api */ class MemberService extends BaseService { protected static $instance; /** * 构造函数 * @author laravel开发员 * @since 2020/11/11 */ public function __construct() { $this->model = new MemberModel(); } /** * 静态入口 */ public static function make(){ if(!self::$instance){ self::$instance = new static(); } return self::$instance; } /** * 登录注册 * @param $walletUrl * @param string $scode * @return array|false */ public function loginOrRegister($walletUrl, $scode='') { if(empty($walletUrl)){ $this->error = 1038; return false; } $info = $this->model->with(['parent'])->where(['wallet_url'=> $walletUrl,'mark'=>1]) ->select(['id','nickname','member_level','performance','code','usdt','sbt','profit','parent_id','wallet_url','wallet_token','recharge_url','bonus_rate','pledge_auto','trade_status','status']) ->first(); $info = $info? $info->toArray() : []; $parentInfo = isset($info['parent'])? $info['parent'] : []; $parentWalletUrl = isset($parentInfo['wallet_url'])? $parentInfo['wallet_url'] : ''; $ip = get_client_ip(); $ipData = ToolService::make()->getIpAddress($ip,''); $province = isset($ipData['regionName'])? $ipData['regionName'] : ''; $city = isset($ipData['city'])? $ipData['city'] : ''; /* TODO 注册 */ if(empty($info)){ // 上级账号 $parentId = 0; $parentIds = ''; if($scode){ $parentInfo = $this->model->where(['code'=>$scode,'mark'=>1])->select(['id','parent_ids','wallet_url'])->first(); $parentId = isset($parentInfo['id'])? $parentInfo['id'] : 0; $parents = isset($parentInfo['parent_ids'])? $parentInfo['parent_ids'] : ''; $parentWalletUrl = isset($parentInfo['wallet_url'])? $parentInfo['wallet_url'] : ''; if($parentId){ $parentIds = $parents? trim($parents,',').",{$parentId}," : "{$parentId},"; } } // 注册奖励 $awardSbtUsdt = ConfigService::make()->getConfigByCode('register_award_sbt', 0); if($awardSbtUsdt>0) { $sbtPrice = PriceLogService::make()->getSbtPrice(); $awardSbtNum = $sbtPrice ? round($awardSbtUsdt * 1 / $sbtPrice, 2) : 0; } DB::beginTransaction(); $id = $this->model->max('id') + 1; $info = [ 'nickname'=> 'SBT_'.substr($walletUrl,-6,6), 'wallet_url'=> $walletUrl, 'wallet_token'=> make_wallet_token($walletUrl, $id), 'avatar'=> '', 'recharge_url'=> '', 'member_level'=> 0, 'usdt'=> 0.00, 'sbt'=> $awardSbtNum, 'profit'=> 0.00, 'bonus_rate'=> 0.00, 'performance'=> 0.00, 'parent_id'=> $parentId, 'parent_ids'=> $parentIds, 'pledge_auto'=> 1, 'code'=> strtoupper(get_random_code(9, 'S', "{$id}")), 'login_ip' => $ip, 'login_region' => $province.''.$city, 'login_time'=> time(), 'create_time' => time(), 'trade_status'=> 1, 'status'=> 1, 'mark' => 1, ]; if(!$userId = $this->model->insertGetId($info)){ DB::rollBack(); $this->error = 2007; return false; } if ($awardSbtNum > 0) { $log = [ 'user_id' => $userId, 'type' => 13, 'order_no' => get_order_num('RW'), 'coin_type' => 2, 'money' => $awardSbtNum, 'before_money' => 0, 'create_time' => time(), 'remark' => '注册奖励', 'action_ip' => get_client_ip(), 'status' => 1, 'mark' => 1 ]; if(!AccountLogModel::insertGetId($log)){ DB::rollBack(); $this->error = 2007; return false; } } DB::commit(); // 清除团队统计数据或列表缓存 RedisService::keyDel("caches:team*"); }else{ $userId = $info['id']; $status = isset($info['status'])? $info['status'] : 0; if($status != 1){ $this->error = 2015; return false; } $updateData = [ 'login_ip'=> $ip, 'login_region' => $province.''.$city, 'login_time'=> time(), 'update_time'=> time(), ]; if($info['wallet_token'] == ''){ $updateData['wallet_token'] = make_wallet_token($walletUrl, $userId); } $this->model->where(['id'=> $userId])->update($updateData); } // 获取授权TOKEN $jwt = new Jwt('jwt_wx'); $token = $jwt->getToken($userId); RedisService::set("auths:info:{$userId}", $info, 6*3600); $this->error = 2004; return [ 'token'=> $token, 'info'=> [ 'id'=> $userId, 'wallet_url'=> $info['wallet_url'], 'member_level'=> intval($info['member_level']), 'usdt'=> round($info['usdt'],4), 'sbt'=> round($info['sbt'],2), 'profit'=> round($info['profit'],2), 'performance'=> round($info['performance'],2), 'code'=> $info['code'], 'nickname'=> $info['nickname'], 'parent_id'=> $info['parent_id'], 'bonus_rate'=> $info['bonus_rate'], 'pledge_auto'=> $info['pledge_auto'], 'parent_url'=> $parentWalletUrl, ], ]; } /** * 用户信息 * @param int $userId 用户ID * @param int $type 返回数据类型:1-默认,2-充值,3-提现 * @param false $refresh * @return array|mixed */ public function getInfo(int $userId, $type = 1, $refresh=false) { $cacheKey = "caches:member:info_{$userId}_{$type}"; $info = RedisService::get($cacheKey); if($info && !$refresh){ return $info; } $info = $this->model->with(['parent'])->where(['id'=> $userId,'mark'=>1]) ->select(['id','nickname','code','member_level','usdt','sbt','performance','profit','profit_total','pledge_profit','manage_profit','share_profit','pj_profit','global_profit','parent_id','wallet_url','wallet_token','recharge_url','bonus_rate','pledge_auto','trade_status','status']) ->first(); $info = $info? $info->toArray() : []; if(empty($info)){ $this->error = 2016; return false; } $parentInfo = isset($info['parent'])? $info['parent'] : []; $parentWalletUrl = isset($parentInfo['wallet_url'])? $parentInfo['wallet_url'] : ''; $walletUrl = isset($info['wallet_url'])? $info['wallet_url'] : ''; $walletToken = isset($info['wallet_token'])? $info['wallet_token'] : ''; if($type == 3 && $walletUrl && $walletToken != make_wallet_token($walletUrl, $userId)){ $info['error'] = '钱包地址已被修改'; } $rechargeAddress = WalletService::make()->getWallet(1); $rechargeUrl = isset($rechargeAddress['address'])? $rechargeAddress['address'] : ''; $info = [ 'id'=> $userId, 'wallet_url'=> $info['wallet_url'], 'member_level'=> intval($info['member_level']), 'usdt'=> moneyFormat($info['usdt'],2), 'sbt'=> moneyFormat($info['sbt'],2), 'performance'=> moneyFormat($info['performance'],2), 'profit'=> moneyFormat($info['profit'],2), 'profit_total'=> moneyFormat($info['profit_total'],2), 'pledge_total'=> moneyFormat($info['pledge_profit'],2), 'share_profit'=> moneyFormat($info['share_profit'],2), 'manage_profit'=> moneyFormat($info['manage_profit'],2), 'global_profit'=> moneyFormat($info['global_profit'],2), 'pj_profit'=> moneyFormat($info['pj_profit'],2), 'code'=> $info['code'], 'nickname'=> $info['nickname'], 'parent_id'=> $info['parent_id'], 'bonus_rate'=> $info['bonus_rate'], 'pledge_auto'=> $info['pledge_auto'], 'error'=> isset($info['error'])? $info['error'] : '', 'parent_url'=> $parentWalletUrl, 'recharge_url'=> $rechargeUrl, 'max_usdt_rate'=> ConfigService::make()->getConfigByCode('usdt_withdraw_max_rate', 0), 'sbt_fee'=> ConfigService::make()->getConfigByCode('profit_withdraw_sbt_fee', 0), 'recharge_min'=> ConfigService::make()->getConfigByCode('recharge_min_money', 0.1), 'withdraw_min'=> ConfigService::make()->getConfigByCode('withdraw_min_money', 0), ]; if($type == 1){ $chainTotal = BalanceLogService::make()->getCountByType($userId, 1); $ptTotal = AccountLogService::make()->getCountByType($userId, 5, 1); $info['recharge_total'] = moneyFormat($chainTotal + $ptTotal, 2); $chainTotal = BalanceLogService::make()->getCountByType($userId, 2, 0, 0); $ptTotal = AccountLogService::make()->getCountByType($userId, 6, 1); $info['withdraw_total'] = moneyFormat($chainTotal+$ptTotal,2); } RedisService::set($cacheKey, $info, rand(5,10)); return $info; } /** * 获取级数据 * @param $levelId * @param $type 类型:1-当前等级数据,2-下一等级数据 * @return array|mixed */ public function getLevelData($levelId, $type = 1) { $cacheKey = "caches:member:level_{$levelId}_{$type}"; $info = RedisService::get($cacheKey); if($info){ return $info; } $data = MemberLevelModel::where(function($query) use($type, $levelId){ if($type == 2){ $query->where('id','>', $levelId); }else{ $query->where('id', $levelId); } }) ->where(['status'=>1,'mark'=>1]) ->select(['id','name','upgrade_usdt','bonus_rate','weighting_rate']) ->orderBy('sort','asc') ->orderBy('id','asc') ->first(); $data = $data? $data->toArray() : []; if($data){ RedisService::set($cacheKey, $data, rand(3600, 7200)); } return $data; } /** * 设置分红比例 * @param int $userId 用户ID * @param array $params 参数,user_id-设置用户ID(必选),bonus_rate-比例 * @return bool */ public function setBonus(int $userId, array $params) { // 参数验证 $uid = isset($params['user_id'])? intval($params['user_id']) : 0; $bonusRate = isset($params['bonus_rate']) && $params['bonus_rate']>0? floatval($params['bonus_rate']) : 0; if($uid<=0){ $this->error = 2014; return false; } if($bonusRate<=0 || $bonusRate>=100){ $this->error = 1022; return false; } $info = $this->model->where(['id'=> $userId,'mark'=>1])->first(); if(empty($info)){ $this->error = 2016; return false; } // 账号状态 $status = isset($info['status'])? $info['status'] : 0; if($status != 1){ $this->error = 2015; return false; } // 设置用户 $userInfo = $this->model->where(['id'=> $uid,'status'=>1,'mark'=>1]) ->select(['id','parent_id','parent_ids','bonus_rate']) ->first(); if(empty($userInfo)){ $this->error = 1042; return false; } // 上级用户校验 $parentId = isset($userInfo['parent_id'])?$userInfo['parent_id']:0; $parentIds = isset($userInfo['parent_ids'])?$userInfo['parent_ids']: ''; if($parentId != $userId){ $this->error = 1043; return false; } // 最大设置比例 $maxBonusRate = $this->getUpBonusRate($uid, $parentIds); if($maxBonusRate>0 && $bonusRate>$maxBonusRate){ $this->errorData = ['rate'=> $maxBonusRate]; $this->error = 1043; return false; } // 更新数据 $data = [ 'bonus_rate'=> floatval($bonusRate), 'update_time'=> time() ]; if(!$this->model->where(['id'=> $uid])->update($data)){ $this->error = 1020; return false; } $this->error = 1019; return true; } /** * 获取上级团队设置的最近的比例 * @param $userId * @param $parentIds * @return false|int|mixed */ public function getUpBonusRate($userId, $parentIds) { $cacheKey = "caches:member:upRate_{$userId}"; $data = RedisService::get($cacheKey); if($data){ return isset($data['bonus_rate'])? $data['bonus_rate'] : 0; } $parentIds = $parentIds? explode(',', $parentIds) : []; $parentIds = array_filter($parentIds); krsort($parentIds); if(empty($parentIds) || !is_array($parentIds)){ return 0; } $data = $this->model->whereIn('id', $parentIds) ->where('bonus_rate','>',0) ->where(['mark'=>1]) ->select(['id','bonus_rate']) ->orderByRaw('FIELD(id, "' . implode(',', $parentIds). '")') ->first(); $data = $data? $data->toArray() : []; if($data){ RedisService::set($cacheKey, $data, rand(3, 5)); } return isset($data['bonus_rate'])? $data['bonus_rate'] : 0; } /** * 设置自动质押状态 * @param int $userId 用户ID * @param array $params 参数,status-状态(1-开启,0-关闭,必选) * @return bool */ public function setPledge(int $userId, array $params) { // 参数验证 $pledgeStatus = isset($params['status']) ? intval($params['status']) : 0; if (!in_array($pledgeStatus,[0,1])) { $this->error = 2014; return false; } // 关闭限制验证 $cacheKey = "caches:pledge:limitClose_{$userId}"; if($pledgeStatus == 0 && RedisService::get($cacheKey)){ $this->error = 2031; return false; } // 账号信息 $info = $this->model->where(['id' => $userId, 'mark' => 1])->select(['id','pledge_auto','status'])->first(); if (empty($info)) { $this->error = 2016; return false; } // 账号状态 $status = isset($info['status']) ? $info['status'] : 0; if ($status != 1) { $this->error = 2015; return false; } // 质押过,且已经退本过才可关闭 if($pledgeStatus == 0 && !PledgeOrderService::make()->checkPledgeRefund($userId)){ $this->error = 2030; return false; } // 更新 $data = ['pledge_auto'=> $pledgeStatus, 'update_time'=>time()]; if(!$this->model->where(['id'=> $userId])->update($data)){ $this->error =1020; return false; } // 限制关闭次数 if($pledgeStatus == 0){ RedisService::set($cacheKey, date('Y-m-d H:i:s'), 3600); } $this->error =1019; return true; } /** * 保存资料 * @param int $userId 用户ID * @param $params 参数 * @return bool */ public function setProfile(int $userId, $params) { $info = $this->model->where(['id'=> $userId,'mark'=>1])->first(); if(empty($info)){ $this->error = 2016; return false; } $data = [ 'update_time'=> time() ]; if(isset($params['nickname']) && $params['nickname']){ $data['nickname'] = trim($params['nickname']); } // 头像 // 更新 if(!$this->model->where(['id'=> $userId])->update($data)){ $this->error =1009; return false; } $this->error =1008; return true; } /** * 提现 * @param $userId 提现用户ID * @param $params 提现参数:money-金额(必选)、coin_type-账户类型/币种(必选) * @return array|false|mixed */ public function withdraw($userId, $params) { $money = isset($params['money'])? floatval($params['money']) : 0; $coinType = isset($params['coin_type'])? intval($params['coin_type']) : 1; if($money<=0){ $this->error = 2025; return false; } // 锁 $coinType = $coinType==1? 1 : 2; $cacheKey = "caches:withdraw:{$userId}_{$coinType}"; if(RedisService::get($cacheKey.'_lock')){ return false; } $info = $this->model->where(['id'=> $userId,'mark'=>1])->first(); if(empty($info)){ $this->error = 2016; return false; } $status = isset($info['status'])? $info['status'] : 0; $tradeStatus = isset($info['trade_status'])? $info['trade_status'] : 0; $userUsdt = isset($info['usdt'])? floatval($info['usdt']) : 0; $userProfit = isset($info['profit'])? floatval($info['profit']) : 0; $userSbt = isset($info['sbt'])? floatval($info['sbt']) : 0; if($status != 1){ $this->error = 2015; // 冻结 return false; } if(in_array($tradeStatus, [2,3])){ $this->error = intval("202{$tradeStatus}"); return false; } // 可提金额或手续费验证 try { DB::beginTransaction(); if ($coinType == 1) { $maxUsdtRate = ConfigService::make()->getConfigByCode('usdt_withdraw_max_rate', 0); $withdrawTotal = BalanceLogService::make()->getCountByType($userId, 2, 1,0); $maxWithdrawMoney = ($withdrawTotal + $userUsdt); $maxWithdrawMoney = $maxUsdtRate > 0 && $maxUsdtRate < 100 ? moneyFormat($maxWithdrawMoney * $maxUsdtRate / 100, 2) : $maxWithdrawMoney; if (($withdrawTotal + floatval($money)) >= $maxWithdrawMoney) { DB::rollBack(); $this->error = 2026; // 可提金额超出 $this->errorData = ['money' => moneyFormat($maxWithdrawMoney - $withdrawTotal,2)]; return false; } // 提现处理 $updateData = ['usdt' => DB::raw("usdt - {$money}"), 'update_time' => time()]; if (!$this->model->where(['id' => $userId])->update($updateData)) { DB::rollBack(); $this->error = 2033; // 提现处理失败 return false; } // 账单 $log = [ 'user_id' => $userId, 'order_no' => get_order_num('SW'), 'type' => 2, 'user_type' => 1, 'coin_type' => 1, 'money' => $money, 'actual_money' => $money, 'fee' => 0, 'pay_type' => 10, 'pay_status' => 10, 'wallet_url' => $info['wallet_url'], 'pt_wallet_url' => '', 'date' => date('Y-m-d'), 'create_time' => time(), 'audit_remark' => '', 'status' => 1, 'mark' => 1, ]; if (!BalanceLogModel::insertGetId($log)) { DB::rollBack(); $this->error = 2033; return false; } // USDT余额账单 $data = [ 'user_id' => $userId, 'order_no' => $log['order_no'], 'type' => 4, 'user_type' => 1, 'coin_type' => 1, 'money' => -$money, 'before_money' => $userUsdt, 'create_time' => time(), 'action_ip' => get_client_ip(), 'remark' => '余额提现', 'status' => 1, 'mark' => 1, ]; if (!AccountLogModel::insertGetId($data)) { DB::rollBack(); $this->error = 2033; return false; } // 成功 DB::commit(); return $this->getInfo($userId, 3, true); } // 收益提现 else { $sbtUsdtFee = ConfigService::make()->getConfigByCode('profit_withdraw_sbt_fee', 0); $sbtPrice = PriceLogService::make()->getSbtPrice(); $sbtFee = moneyFormat(floatval($sbtUsdtFee) / $sbtPrice, 2); if ($sbtFee > $userSbt) { DB::rollBack(); $this->error = 2032; // 手续费不足 $this->errorData = ['money' => $sbtFee]; return false; } // 余额是否足 if ($money > $userProfit) { DB::rollBack(); $this->error = 2026; // 手续费不足 $this->errorData = ['money' => $userProfit]; return false; } // 提现处理 $updateData = ['profit' => DB::raw("profit - {$money}"), 'sbt' => DB::raw("sbt - {$sbtFee}"), 'update_time' => time()]; if (!$this->model->where(['id' => $userId])->update($updateData)) { DB::rollBack(); $this->error = 2033; // 提现处理失败 return false; } // 账单 $log = [ 'user_id' => $userId, 'order_no' => get_order_num('SW'), 'type' => 2, 'user_type' => 1, 'coin_type' => 2, 'money' => $money, 'actual_money' => $money, 'fee' => $sbtFee, 'fee_usdt' => $sbtUsdtFee, 'pay_type' => 10, 'pay_status' => 10, 'wallet_url' => $info['wallet_url'], 'pt_wallet_url' => '', 'date' => date('Y-m-d'), 'create_time' => time(), 'audit_remark' => '', 'status' => 1, 'mark' => 1, ]; if (!BalanceLogModel::insertGetId($log)) { DB::rollBack(); $this->error = 2033; return false; } // 收益账单 $data = [ 'user_id' => $userId, 'order_no' => $log['order_no'], 'type' => 4, 'user_type' => 1, 'coin_type' => 3, 'money' => -$money, 'before_money' => $userProfit, 'create_time' => time(), 'action_ip' => get_client_ip(), 'remark' => '收益提现', 'status' => 1, 'mark' => 1, ]; if (!AccountLogModel::insertGetId($data)) { DB::rollBack(); $this->error = 2033; return false; } // 手续费账单 if($sbtFee>0){ $data = [ 'user_id' => $userId, 'order_no' => $log['order_no'], 'type' => 4, 'user_type' => 1, 'coin_type' => 2, 'money' => -$sbtFee, 'before_money' => $userSbt, 'create_time' => time(), 'action_ip' => get_client_ip(), 'remark' => '提现手续费', 'status' => 1, 'mark' => 1, ]; if (!AccountLogModel::insertGetId($data)) { DB::rollBack(); $this->error = 2033; return false; } } // 成功 DB::commit(); return $this->getInfo($userId, 3, true); } } catch (\Exception $exception){ DB::rollBack(); $this->errorData = ['error'=> $exception->getMessage()]; $this->error = 2028; return false; } } }