IT小白的第一個微信小程序(即刻翻譯)

第一次嘗試寫文章,文筆可能很差,請見諒;php

  •  自學過一個月的PHP(從零開始);
  • 自學過一個月的Web前端知識(從零開始,html+css+js);
  • 自學一個月的Centos操做系統(從零開始,本人英語極差,常常須要有道翻譯~);

由於即將頂崗實習,因此決定擼一個實際上線的小程序(即刻翻譯);css

嗯......想說明的一點是,因爲我審美觀確實不咋地,因此UI界面很是醜(請不要介意!);html


小程序思路:

小程序動畫界面:前端

1. onLoad: 關閉調試接口,用戶點擊是否受權我的信息;json

2. onShow:判斷本地緩存是否存在Token,如未存在則調用 user 接口獲取Token並緩存於本地;小程序

3.作完動畫跳轉到翻譯界面;
api


小程序翻譯界面:數組

  1. Picker的Change事件;
  2. 模擬節流函數防止用戶屢次點擊(throttle);
  3. 點擊翻譯時判斷input的值是否爲空或者是純空格;
  4. 紅色圓×的點擊事件:清除input;
  5. 翻譯按鈕的wx.request請求(須要翻譯的內容與目標語種);
  6. 翻譯成功後將返回的dst顯示在最下面的scroll-view;




PHP:

框架使用的ThinkPHP5.1(第一次接觸的php框架);緩存


驗證層(validate):

 1.結構:
php框架

 2.代碼:

BaseValidate.php:

use app\lib\exception\ParameterException;
use think\Validate;

class BaseValidate extends Validate
{ 
   public function goCheck()
    { 
       //對傳入的全部參數作校驗 

      獲取請求的全部參數;       
      $params = \request()->param();  

      全部請求作驗證
      $result = $this->batch()->check($params);

      若是不爲true,拋出參數錯誤異常       
      if(!$result)
        {throw new ParameterException(['msg'=>$this->error*]);} 
      else
        {return true;}
     }
    
    public function isNoEmpty($value,$rule='',$data='',$field='')   
     { 
        若是值爲空,直接返回false;       
        if(empty($value))        
            {return false;}
        else
            {return true;}    
      }
}


定義一個BaseValidate繼承自TP的的Validate;
定義一個方法用來作requset請求(GET,POST,DELETE....)的參數驗證;
定義方法 isNoEmpty(驗證規則,驗證參數不爲空);
複製代碼


CacheClean.php:

class CacheCleaner extends BaseValidate
{    
    protected $rule=[        'AdministratorKey'=>'require|isNoEmpty'    ];    
    protected $message=[        'AdministratorKey'=>'請攜帶正確的管理員密鑰'    ];
}



定義類 CacheClearner繼承自BaseValidate,管理員用來清除Token文件;複製代碼

Token.php:

class Token extends BaseValidate
{    
    protected $rule =[        'code'=>'require|isNoEmpty'    ];    
    protected $message=[        'code'=>'code必須輸入且不能爲空'    ];
}



定義類 Token 繼承自BaseValidate;


這裏的Code須要傳入的是 wx.login返回的Code碼 
(用戶登陸憑證(有效期五分鐘)。
開發者須要在開發者服務器後臺調用 api,
使用 code 換取 openid 和 session_key 等信息);複製代碼

Translate.php:

class Translate extends BaseValidate
{    
    protected $rule=[        'token'=>'require|isNoEmpty',        'q'=>'require|isNoEmpty',        'from'=>'require|isNoEmpty',        'to'=>'require|isNoEmpty',    ];    
    protected $message=[        'token'=>'token必須輸入且不能爲空',        'q'=>'q必須輸入且不能爲空',        'from'=>'from必須輸入且不能爲空',        'to'=>'to必須輸入且不能爲空',    ];
}

定義類Translate 繼承自BaseValidate;
驗證token,驗證傳遞來的q(被翻譯文本),from(來源語種),to(目標語種)複製代碼



服務層(service):

1.結構:


1.代碼:

Token.php:

use app\lib\exception\TokenException;
class Token
{    
        /**     
         * 根據token_salt和隨機字符串生成32位MD5字符串;    
         * @return string     
         */    
        public static function generaeToken()   
        {        
            //32個字符組成一組隨機字符串   

             生成一段隨機字符串(getRandChar定義在common公共文件裏面);    
            $randChars = getRandChar(32);
            
            鹽寫在配置文件裏.
            $salt = config('token_salt');
       
            返回MD5(隨機字符串+鹽)的 Token;
            return md5($randChars . $salt);   
          }  

         /**     
          * 檢驗cache緩存中是否存在傳遞來的token     
          * @throws TokenException     
          */    
        public static function needToken()    
        {   
            獲取本次請求的token;
            $token = request()->param('token');        
            

            若是緩存文件中不存在傳來的token,拋異常
            $result = cache($token);       
            if (!$result){            
            throw new TokenException();       
            }    
        }
}


複製代碼

Translate.php:

偷懶使用百度寫好的php文件(很容易理解,不過爲了快偷懶了......);

use app\lib\exception\BaiduException;
class Translate
{    
    protected $query;   
    protected $from;    
    protected $to;    


/**    
 * 構造 傳遞q ,from .to     
* Translate constructor.     
* @param $query     
* @param $from     
* @param $to     
*/    
    public function __construct($query,$from,$to)    
    {        
        $this->query=$query;        
        $this->from=$from;        
        $this->to=$to;    
    }    
/**     
* 主方法     
* @return bool|mixed     
*/    
    public function translate()   
    {        
        $args = array('q' => $this->query,'appid' => config('Bapp_id'),'salt' => config('B_salt'), 'from' => $this->from,'to' => $this->to,);
        $args['sign'] = $this->buildSign($this->query, config('Bapp_id'), $args['salt'], config('Bapp_secret'));        
        $ret = $this->call(config('Blogin_url'), $args);       
        $ret = json_decode($ret,true);       
        $this->checkApiHasError($ret);        
        return  $this->RecombinedData($ret);    
    }   

    
    /**     
    * 檢測接口是否調用失敗     
    * @param array $array     
    * @throws BaiduException     
    */    
    public function checkApiHasError(array $array)
    {        
        if(array_key_exists('error_code',$array)){throw new BaiduException(['msg'=>$array['error_msg'],'errorCode'=>$array['error_code']]);
    }    
    } 
   
    /**     
    * 生成簽名    
    * @param $query     
    * @param $appID     
    * @param $salt     
    * @param $secKey     
    * @return string     
    */    
    public function buildSign($query, $appID, $salt, $secKey)
    {        
        $str = $appID . $query . $salt . $secKey;        
        $ret = md5($str);        
        return $ret;    
    }   


    /**     
    * 發起請求     
    * @param $url     
    * @param null $args     
    * @param string $method     
    * @param int $testflag     
    * @param int $timeout     
    * @param array $headers     
    * @return bool|mixed     
    */    
    public function call($url, $args=null, $method="post", $testflag = 0, $timeout = 10, $headers=array())    
    {        
        $ret = false;        
        $i = 0;        
        while($ret === false)
        {            
        if($i > 1) break;            
        if($i > 0){sleep(1);}            
        $ret = $this->callOnce($url, $args, $method, false, $timeout, $headers); $i++;}        
        return $ret;   
    }  

    /**     
    * 發起請求,判斷是post方式仍是get方式     
    * @param $url     
    * @param null $args     
    * @param string $method     
    * @param bool $withCookie     
    * @param int $timeout     
    * @param array $headers     
    * @return mixed     
    */   
    public function callOnce($url, $args = null, $method = "post", $withCookie = false, $timeout = 10, $headers = array())
    {
        $ch = curl_init();
        if ($method == "post")
        {
            $data = $this->convert($args);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt($ch, CURLOPT_POST, 1);
        } else{
            $data = $this->convert($args);
            if ($data)
            {
            if (stripos($url, "?") > 0) {$url .= "&$data";} else{$url .= "?$data";}            
            }        
        }  
     
     curl_setopt($ch, CURLOPT_URL, $url);       
     curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);     
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
     if (!empty($headers))  
     {          
         curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);  
     }        
    if ($withCookie) 
     {   
         curl_setopt($ch, CURLOPT_COOKIEJAR, $_COOKIE);     
     }       
     $r = curl_exec($ch);
     curl_close($ch);
     return $r;    

    }  


    /**     
    * URL encode 各個字段     
    * @param $args     
    * @return string     
    */    
    public function convert(&$args)
    {
        $data = '';
        if (is_array($args))
        {
            foreach ($args as $key => $val)
            {
                if (is_array($val))
                {
                    foreach ($val as $k => $v)
                    {
                        $data .= $key . '[' . $k . ']=' . rawurlencode($v) . '&';
                    }                
                 } else{
                    $data .= "$key=" . rawurlencode($val) . "&";
                 }
            }           
           return trim($data, "&");
        }
        return $args;
    }     


    /**     
    * 重組數據     
    * @param $array     
    * @return array     
    */    
    public function RecombinedData($array)
    {
        $trans_result = $array['trans_result'];        
        $trans_result = $trans_result[0];        
        $src = $trans_result['src'];        
        $dst = $trans_result['dst'];        
        unset($trans_result);        
        return $data = ['from'=>$array['from'],'to'=>$array['to'],'src'=>$src,'dst'=>$dst];
    }

}複製代碼


UserToken.php:

use app\lib\exception\WeChatException;
class UserToken extends Token{
    protected $code;    
    protected $wxAppID;    
    protected $wxAppSecret;    
    protected $wxLoginUrl; 

   
    /**     
    * 構造 傳入小程序code     
    * UserToken constructor.    
    * @param $code     
    */    
    public function __construct($code)    
    {   
        wx.login返回的Code     
        $this->code = $code;

        小程序開發者的app_id        
        $this->wxAppID = config('app_id');

        小程序開發者的app_secret         
        $this->wxAppSecret = config('app_secret');
        
        把三個變量依次寫入login_url中        
        $this->wxLoginUrl = sprintf(config('login_url'), 
        $this->wxAppID, $this->wxAppSecret, $this->code);    
    }    



    /**     
    * 主方法     
    * @return string     
    * @throws WeChatException     
    */    
    public function get()   
    { 
        封裝curl在common.php公共文件中(這裏就不貼出來);     
      $result = curl_get($this->wxLoginUrl);   
      
        返回結果json轉數組;    
      $wxResult = json_decode($result, true); 
        
        若是結果爲空,拋異常       
      if (!$wxResult){throw new Exception('獲取session_key及openID時異常,微信內部錯誤');} 
        
        若是數組索引存在errcode,拋異常,不然生成令牌寫入緩存並返回令牌      
      if (array_key_exists('errcode', $wxResult))       
      {$this->processLoginError($wxResult);} 
      else      
      {return $this->grantToken($wxResult);}    
    }    




    /**     
    * 生成令牌寫入緩存並返回令牌     
    * @param $wxResult     
    * @return string     
    */    
    private function grantToken($wxResult)
    {        
      //生成令牌 準備緩存數據 寫入緩存        
      //把令牌返回給客戶端        
      //key :令牌        
      //value:wxResult;
        
      $token=$this->savaToCache($wxResult);        
      return $token;    
    }    


    /**     
    * 寫入緩存  
    * @param $wxResult     
    * @return string     
    */    
    private function savaToCache($wxResult)
    {   
        鍵爲token;
        $key=self::generaeToken();  
        
        值爲openid等等      
        $value=json_encode($wxResult);    

        緩存文件過時時間爲0(永不過時);    
        $expire_in=0;  
        
        寫入緩存      
        $requst=cache($key,$value,$expire_in);  
    
        寫入不成功拋異常      
        if(!$requst){throw new TokenException(['msg'=>'服務器緩存異常','errorCode'=>10005]);}
        
        返回key(token);        
        return $key;    
    }    


    /**     
    * 拋出code已被使用或是無效的code 的錯誤     
    * @param $wxResult     
    * @throws WeChatException     
    */    
    private function processLoginError($wxResult)   
     {        
    throw new WeChatException(['msg' => $wxResult['errmsg'],'errorCode' => $wxResult['errcode']]);    
    }
}複製代碼


控制器層(Controller):

1.結構:



1.代碼:

BaseController.php:

use think\Controller;use app\api\service\Token as TokenService;
class BaseController extends Controller
{    
    驗證Token
    public function checkHaveToken()    
    {        
    TokenService::needToken();    
    }
}

定義BaseController繼承自TP5的Controller
複製代碼

Token.php:

use app\api\service\UserToken;
use app\api\validate\CacheCleaner;
use app\api\validate\Token as TokenValidate;
use app\lib\exception\ParameterException;
use think\facade\Cache;use think\facade\Request;
class Token extends BaseController{    
//根據客戶端傳來的code 生成token並返回    
    public function getToken()    
    {        
    //驗證        
    (new TokenValidate())->goCheck();
    //邏輯        
    $token = (new UserToken(Request::param('code')))->get();
    //返回        
    return ['token' => $token];    
    }    


    //測試接口    
    public function cleanAllCache()    
    {
    //驗證        
    (new CacheCleaner())->goCheck();  
    //邏輯      
    if(!(Request::get('AdministratorKey')===config('app.adminKey')))     
   {throw new ParameterException(['msg'=>'請攜帶正確的管理員密鑰']);}
    
    Cache::clear();
    //返回
    return '清除緩存文件成功';
    }
}複製代碼

Translate.php:

use app\api\validate\Translate as TranslateValidate;
use app\api\service\Translate as TranslateService;
use think\facade\Request;
class Translate extends BaseController
{    
    //前置操做    
    protected $beforeActionList=['checkHaveToken'=>['only'=>'translate']];    

    //根據客戶端傳來的token作檢驗,檢驗成功則調用百度翻譯接口並返回接口數據給客戶端    
    public function translate()    
    {
    //驗證        
    (new TranslateValidate())->goCheck();        
    //邏輯        
    $res=(new TranslateService(Request::param('q'),Request::param('from'),Request::param('to')))->translate();       
     //返回        
    return $res;    
    }
}複製代碼



異常(Exception):

1.結構:

2.代碼:

ExceptionHandler.php:

use think\exception\Handle;
use think\Log;
class ExceptionHandler extends Handle
{  
  
    /**    
    * 自定義全局異常處理     
    */    
    private $code;    
    private $msg;    
    private $errorCode; 
   
    /**     
    * 根據異常類型判斷是否寫入日誌 並返回客戶端當前請求的url路徑     
    * @param \Exception $e     
    * @return \think\Response|\think\response\Json     
    */    
    public function render(\Exception $e)    
    {        
        if ($e instanceof BaseException)        
        {            
            //若是是自定義的異常            
            $this->code = $e->code;            
            $this->msg = $e->msg;            
            $this->errorCode = $e->errorCode;        
        } else{            
            return parent::render($e);        
        }        
        $result = ['msg' => $this->msg,'error_code' => $this->errorCode,'request_url' => \request()->url()];        
        return json($result, $this->code);    
    }    


    /**     
    * 寫入錯誤日誌     
    * @param \Exception $e     
    */    
    public function recordErrorLog(\Exception $e)
    {        
        Log::init(['type' => 'File','path' => LOG_PATH,'level' => ['error']]);        
        Log::record($e->getMessage(), 'error');    
    }


}複製代碼


BaseException.php:

use think\Exception;
class BaseException extends Exception{    
    //HTTP狀態碼    
    public $code = 400;    
    //錯誤信息    
    public $msg = '錯誤';    
    //自定義錯誤碼    
    public $errorCode = 10000;    

    /**     
    * 根據錯誤生成錯誤http狀態碼,錯誤信息,和錯誤碼     
    * BaseException constructor.     
    * @param array $params     
    */    
    public function __construct($params = [])    
    {        
        if(!is_array($params))        
        {            
            return ;        
        }        
        if(array_key_exists('code',$params)){$this->code=$params['code'];        }        
        if(array_key_exists('errorCode',$params)){$this->errorCode=$params['errorCode'];}        
        if(array_key_exists('msg',$params)){$this->msg=$params['msg'];}    
    }


}



定義BaseException繼承自TP5的Exception複製代碼

BaiduException.php:

class BaiduException extends BaseException
{    
    public $code=401;    
    public $msg='接口調用錯誤啊個人天';    
    public $errorCode=10000;
}複製代碼

WeChatException.php:

class WeChatException extends BaseException
{   
    public $code = 400;    
    public $msg = '微信服務接口調用失敗';    
    public $errorCode = 999;
}複製代碼

TokenException.php:

class TokenException extends BaseException
{    
    public $code=401;    
    public $msg='Token過時或無效Token,請從新獲取';    
    public $errorCode=10000;
}複製代碼

ParameterException.php:

class ParameterException extends BaseException
{   
    public $code = 400;    
    public $msg = '參數錯誤';    
    public $errorCode = 10000;
}複製代碼



公共文件(common.php):

/**
    * 封裝http請求 
    * @param $url 
    * @param int $httpCode 
    * @return mixed 
    */
    function curl_get($url, $httpCode = 0)
    {
        $ch = curl_init();    
        curl_setopt($ch, CURLOPT_URL, $url);    
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
        //不作證書校驗,部署在Linux環境下請改成true    
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);    
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);    
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);   
        $file_contents = curl_exec($ch);    
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
        curl_close($ch);    
        return $file_contents;
    }


    /** 
    * 生成隨機字符串 
    * @param $length 
    * @return null|string 
    */
    function getRandChar($length)
    {    
        $str = null;    
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";    
        $max = strlen($strPol) - 1;    
        for ($i = 0; $i < $length; $i++)    
        {        
            $str .= $strPol[rand(0, $max)];    
        }    
            return $str;
    }複製代碼


最後把項目部署到服務器上(我在阿里雲購買的學生機和域名);

中間還遇到各類坑.(搭建環境,配置ssh證書等等);

反正很麻煩就對啦.



嗯...差很少就是這樣,獻醜了,嘻嘻.

                                                                                                     _______   陳三好

相關文章
相關標籤/搜索