多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## BaseApi.php ~~~ namespace app\api\controller; use think\App; use think\captcha\CaptchaWithRedis; use think\Cache; use think\Response; use think\Db; use think\Controller; use think\Request; use app\admin\model\Users as UsersModel; class BaseApi extends Controller { public $send_scene; const PC_SMS_CODE_PREFIX = 'cookie_sms_code_';// 短信验证码缓存前缀 const CODE_EXPIRE = 180;// 短信及图形验证码有效时间180s const TOKEN_EXPIRE_TIME = 86400;// token过期时间 86400 const PC_BIND_HASH_KEY = 'pc_bind_token_tag'; const USER_WITHOUT_SINGLE_DEVICE_HASH_KEY = 'user_group_without_single_device'; protected $token = ''; protected $domain = ''; //设备类型 protected $deviceType = ''; protected $version = ''; protected $apiVersion; //用户 id protected $userId = 0; protected $request; //用户 protected $user; protected $distribut_level; protected $app; //用户类型 protected $userType; protected $allowedDeviceTypes = ['mobile']; private $salt = 'ddddddddddddddddddddd'; const CURRENT_REQUEST_KEY = 'pc_current_request_num'; const CURRENT_MAX_REQUEST_NUM = 300; public function __construct(App $app = null) { $this->token; $this->request = Request::instance(); $this->domain = Request::instance()->domain(); $this->_initUser(); } // 初始化 protected function initialize() { header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Methods:POST,GET"); header("Access-Control-Allow-Headers:ATJ-Device-Type,ATJ-Token"); header("Content-type:text/json;charset=utf-8"); } private function _initUser() { $token = $this->request->header('ATJ-Token'); $deviceType = $this->request->header('ATJ-Device-Type'); $controller = $this->request->controller() ?: ''; if (empty($deviceType) || ! in_array($deviceType, $this->allowedDeviceTypes)) { $this->success('获取成功0'); } $analysis_data = [ 'controller_name' => $controller, 'action_name' => $this->request->action() ?: '', 'token' => $token ?: '', 'page_path' => $this->request->baseUrl(true), 'page_query' => $this->request->param(), 'user_agent' => $this->request->header('user-agent'), 'ip_address' => get_ip() ]; if ($this->withoutTokenPass($analysis_data)) { return true; } if (empty($token)) { $this->error('请先登录'); } $this->token = $token; $user_id = $this->userTokenDecode(); if ( ! $user_id) { $this->error('请先登录'); } $user = UsersModel::where('user_id', $user_id)->find(); if (empty($user)) { $this->error('会员不存在'); } $this->userId = $user['user_id']; $this->distribut_level = $user['distribut_level']; } private function userTokenDecode() { $token = $this->token; if (empty($token)) { $this->error('请先登录'); } if (Cache::has($token)) { $token_str = Cache::get($token); if (is_numeric($token_str)) { $user_id = $token_str; $token_arr = [ 'user_id' => $user_id, 'create_time' => time(), 'last_login' => time(), 'last_ip' => get_ip(), 'update_time' => time() ]; $this->updateTokenExpire($token, json_encode($token_arr), self::TOKEN_EXPIRE_TIME); } else { $token_arr = json_decode($token_str, true); if (is_array($token_arr) && count($token_arr)) { $user_id = $token_arr['user_id']; UsersModel::where('user_id', $user_id)->data([ 'token' => $token, 'token_time' => time() ])->update(); } else { $user_id = 0; } } } else { $user_id = 0; } return $user_id; } protected function setExpireKey($key_name, $message, $expire_time = 60) { if (Cache::has($key_name)) { $this->error($message); } else { Cache::set($key_name, 1, $expire_time); } return true; } protected function deleteExpireKey($key_name) { if (Cache::has($key_name)) { Cache::rm($key_name); } return true; } protected function getCaptchaCode($key) { $config = ['useCurve' => true, 'useNoise' => true];// 验证码设置 $captcha = new CaptchaWithRedis($key, self::CODE_EXPIRE, $config); return $captcha->entry(); } /** * 获取当前的response 输出类型 * @access protected * @return string */ protected function getResponseType() { return 'json'; } protected function deleteRequestNum() { $cache_key = self::CURRENT_REQUEST_KEY; // 如果缓存只存在且大于0,则将缓存减1 if (Cache::has($cache_key) && Cache::get($cache_key) > 0) { Cache::dec($cache_key, 1); } return true; } // 生成用户token protected function createToken($user_id) { $token = md5('base_user_'.$user_id.'_'.time()); return $token; } // 生成用户token protected function createTokenWithTime($user_id, $time) { if ($time) { $token = md5('base_user_'.$user_id.'_'.$time); } else { $token = md5('base_user_'.$user_id.'_'.time()); } return $token; } // 更新token缓存时间 protected function updateTokenExpire($token, $user_id, $time = 0) { if ( ! $token || ! $user_id) { return false; } return Cache::set($token, $user_id, $time); } // 更新token_time protected function updateTokenTime($user, $time, $ip_address) { $user->last_login_time = $time; $user->last_login_ip = $ip_address; $user->token_time = $time; $user->save(); return $user; } protected function createInviteCode($user_id) { return md5(md5($user_id.random(4).time())); } protected function withoutTokenPass($analysis) { if (empty($analysis)) { return false; } if (isset($analysis['controller_name']) && in_array($analysis['controller_name'], ['Login', 'Captcha'])) { return true; } if ($analysis['controller_name'] == 'Index') { $ActionName = ['category', 'article_list', 'article_article', 'banner_list', 'active_page', 'about_us']; if (isset($analysis['action_name']) && in_array($analysis['action_name'], $ActionName)) { return true; } } return false; } } ~~~ ## login.php ~~~ class Login extends BaseApi { const SMS_TEMPLATE_LOGIN = 'SMS_1111111111'; const SMS_CODE_EXPIRE = 180;// 短信验证码有效时间180s const VERIFY_CODE_EXPIRE = 300;// 短信验证码有效时间300s const TX_APPID = '88888888888'; const TX_AppSecretKey = 'gggggggggggggggggggggggg**'; /** * @title 手机验证码登录 * @url /api/login/login * @method POST * * @param string phone 手机号 * @param string code 手机验证码 * @param string invite_code 邀请码 空 允许 * * @header string ATJ-Device-Type 设备类型 空 必须 * @code 1 成功 * @code 0 失败 * @json {"code":1,"msg":"登陆成功","data":{"token":"48df4682bf422797d1d6a3e6c8b7b983","token_time":1617967109,"user_info":{"user_id":11,"nickname":"","sex":0,"mobile":"16637102876","reg_time":1617956763,"last_login":1617967052,"last_ip":"127.0.0.1","head_pic":"","distribut_money":"0.00","underling_number":0,"distribut_level":3}}} * @return int status 状态码 (具体参见状态码说明) * @return string msg 提示信息 */ public function login() { $validate = new \think\Validate([ 'phone' => 'require', 'code' => 'require' ]); $validate->message([ 'phone.require' => '请输入手机号', 'code.require' => '请输入短信验证码' ]); $data = $this->request->post(); if ( ! $validate->check($data)) { return json(['code' => 0, 'msg' => $validate->getError()]); } $cache_key = 'login_pc_phone_'.$data['phone']; $cache_sms_time = 'cookie_sms_time_'.$data['phone']; //$this->setExpireKey($cache_key, '登录中,请稍侯...'); if (cmf_check_mobile($data['phone'])) { $key = self::PC_SMS_CODE_PREFIX.$data['phone']; if (Cache::has($key)) { if ($data['code'] != Cache::get($key)) { $this->deleteExpireKey($cache_key); return json(['code' => 0, 'msg' => '请输入正确的验证码']); } } else { $this->deleteExpireKey($cache_key); return json(['code' => 0, 'msg' => '验证码已失效,请重新获取']); } Cache::rm($key); Cache::rm($cache_sms_time); Cache::rm($this->token); } else { $this->deleteExpireKey($cache_key); return json(['code' => 0, 'msg' => '请输入正确手机号']); } $ip_address = get_ip(); $findId = 0; $leader = 0; $first_leader = 0; $second_leader = 0; $third_leader = 0; if ( ! empty($data['invite_code'])) { $findUsersss = UsersModel::where('invite_code', $data['invite_code'])->find(); $findId = $findUsersss['user_id']; if (empty($findId)) { $findId = 0; } $leader = ! empty($findUsersss['distribut_level']) ? $findUsersss['distribut_level'] : 3; } $distribut_level = 3; switch ($leader) { case 1: $is_distribut = 1; $first_leader = $findId; break; case 2: $is_distribut = 0; $second_leader = $findId; break; default: $is_distribut = 0; $third_leader = $findId; break; } //判断会员 存在,如果不存在则注册 $time = time(); $findUser = UsersModel::where('mobile', $data['phone'])->find(); $token = $this->createToken($findUser['user_id']); if ( ! empty($findUser)) { $upData = [ 'last_login' => $time, 'last_ip' => $ip_address, 'update_time' => $time, 'token_time' => $time, 'token' => $token ]; UsersModel::where('mobile', $data['phone'])->data($upData)->update(); $msg = '登陆成功'; } else { //判断客户是否为老用户(客户表对应手机号,有为二级分销商,否则为普通会员) $findCustomer = CustomerModel::where('contact_phone', $data['phone'])->find(); if ( ! empty($findCustomer)) { $distribut_level = 2; $second_leader = 0; $third_leader = 0; } else { $distribut_level = 3; } $insData = [ 'password' => cmf_password($data['phone']), 'reg_time' => $time, 'last_login' => $time, 'last_ip' => $ip_address, 'mobile' => $data['phone'], 'mobile_validated' => 1, 'distribut_level' => $distribut_level, 'is_distribut' => $is_distribut, 'first_leader' => $first_leader, 'second_leader' => $second_leader, 'third_leader' => $third_leader, 'perpetual' => 0, 'invite_code' => $this->createInviteCode($time), 'create_time' => $time, 'update_time' => $time ]; $userId = UsersModel::insertGetId($insData); $createToken = $this->createTokenWithTime($userId, $time); UsersModel::where('user_id', $userId)->data([ 'token' => $createToken, 'token_time' => $time ])->update(); $findUser = UsersModel::where('user_id', $userId)->find(); $msg = '注册成功'; if ( ! empty($findId) && (in_array($leader, [1, 2]))) { //给上级增加奖励 $this->user_award($findId, 1, $findUser['user_id'], $findUser['mobile']); } } $result = $this->updateTokenExpire($token, $findUser['user_id'], self::TOKEN_EXPIRE_TIME); if ( ! $result) { $this->deleteExpireKey($cache_key); return json(['code' => 0, 'msg' => '登录失败,请重试!']); } $userInfo = [ 'token' => $token, 'token_time' => $findUser['token_time'], 'user_info' => [ 'user_id' => $findUser['user_id'], 'nickname' => ! empty($findUser['nickname']) ? $findUser['nickname'] : '', 'sex' => $findUser['sex'], 'mobile' => $findUser['mobile'], 'reg_time' => $findUser['reg_time'], 'last_login' => $findUser['last_login'], 'last_ip' => $findUser['last_ip'], 'head_pic' => ! empty($findUser['head_pic']) ? $findUser['head_pic'] : '', 'distribut_money' => $findUser['distribut_money'], 'underling_number' => $findUser['underling_number'], 'distribut_level' => $findUser['distribut_level'] ] ]; return json(['code' => 1, 'msg' => $msg, 'data' => $userInfo]); } /** * @title 发送短信验证码 * @url /api/login/send_sms_code * @header string ATJ-Device-Type 设备类型 空 必须 * * @param string phone 手机号 空 必须 * @param string ticket ticket 空 必须 * @param string randstr randstr 空 必须 * @method POST * * @code 1 成功 * @code 0 失败 * @json {"code":1,"msg":"验证码发送成功"} * @return int status 状态码 (具体参见状态码说明) * @return string msg 提示信息 */ public function send_sms_code() { $config = [ 'accessKeyId' => tpCache('sms.sms_appkey'), 'accessKeySecret' => tpCache('sms.sms_secretKey'), 'signName' => tpCache('sms.sms_product'), 'templateCode' => self::SMS_TEMPLATE_LOGIN ]; $param = input('post.'); $mobile = ! empty($param['phone']) ? $param['phone'] : ''; if (empty($mobile)) { return json(['code' => 0, 'msg' => '手机号不能为空']); } $host = 'captcha.tencentcloudapi.com'; $params = [ 'CaptchaType' => 9, 'Ticket' => $param['ticket'], 'UserIp' => get_ip(), 'Randstr' => $param['randstr'], 'CaptchaAppId' => self::TX_APPID, 'AppSecretKey' => self::TX_AppSecretKey ]; $headers = $this->makeHeader($host, $params); try { $result = self::curl_post('https://'.$host, json_encode($params), $headers); } catch (Exception $e) { $this->error($e->getCode(), ['code' => '']); } $result = json_decode($result, true); /*if (empty($result) || ! isset($result['Response']) || ! isset($result['Response']['CaptchaCode']) || $result['Response']['CaptchaCode'] != 1) { $this->error(isset($result['Response']) && isset($result['Response']['CaptchaMsg']) && ! empty($result['Response']['CaptchaMsg']) ? $result['Response']['CaptchaMsg'] : "验证失败,请重试"); }*/ //获取时间配置 $sms_time_out = tpCache('sms.sms_time_out'); $sms_time_out = $sms_time_out ? $sms_time_out : 120; $code = rand(100000, 999999); $cookie_sms_code_time = Cache::get('cookie_sms_time_'.$mobile); //120秒以内不可重复发送 if ( ! empty($cookie_sms_code_time && (time() - $cookie_sms_code_time < $sms_time_out))) { return json(['code' => $code, 'msg' => $sms_time_out.'秒内不允许重复发送']); } $SmsAli = new SmsAli($config); Cache::set('cookie_sms_code_'.$mobile, $code, 300); Cache::set('cookie_sms_time_'.$mobile, time(), 300); $cookie_code = Cache::get('cookie_sms_code_'.$mobile); $sendSms = $SmsAli->send_sms($mobile, json_encode(['code' => $cookie_code])); if ($sendSms['Code'] == 'OK') { return json(['code' => 1, 'msg' => '验证码发送成功', 'yzm' => $cookie_code]); } else { return json(['code' => 0, 'msg' => $sendSms['Message']]); } } private function makeHeader($host, $params) { $headers = [ 'X-TC-Action' => 'DescribeCaptchaResult', 'X-TC-Timestamp' => time(), 'X-TC-Version' => '2019-07-22', 'Content-Type' => 'application/json', 'Host' => $host ]; $algo = "TC3-HMAC-SHA256"; $date = gmdate("Y-m-d", $headers["X-TC-Timestamp"]); $service = 'captcha'; $canonicalUri = '/'; $reqmethod = 'POST'; $canonicalQueryString = ''; $canonicalHeaders = "content-type:".$headers["Content-Type"]."\n"."host:".$headers["Host"]."\n"; $signedHeaders = "content-type;host"; $payloadHash = hash("SHA256", json_encode($params)); $canonicalRequest = $reqmethod."\n".$canonicalUri."\n".$canonicalQueryString."\n".$canonicalHeaders."\n".$signedHeaders."\n".$payloadHash; $credentialScope = $date."/".$service."/tc3_request"; $hashedCanonicalRequest = hash("SHA256", $canonicalRequest); $str2sign = $algo."\n".$headers["X-TC-Timestamp"]."\n".$credentialScope."\n".$hashedCanonicalRequest; $secretKey = 'hhhhhhhhhhhhhhhhhhhhhhhh'; $signature = $this->signTC3($secretKey, $date, $service, $str2sign); $sid = 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'; $auth = $algo." Credential=".$sid."/".$credentialScope.", SignedHeaders=content-type;host, Signature=".$signature; $headers["Authorization"] = $auth; $temp = []; foreach ($headers as $key => $value) { array_push($temp, $key.':'.$value); } return $temp; } protected function signTC3($skey, $date, $service, $str2sign) { $dateKey = hash_hmac("SHA256", $date, "TC3".$skey, true); $serviceKey = hash_hmac("SHA256", $service, $dateKey, true); $reqKey = hash_hmac("SHA256", "tc3_request", $serviceKey, true); return hash_hmac("SHA256", $str2sign, $reqKey); } protected function curl_post($post_url, $post_data, $header = []) { $ch = curl_init(); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_URL, $post_url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $output = curl_exec($ch); curl_close($ch); return $output; } } ~~~